The Dynamo Primer
Now that we’ve demonstrated how to use Python scripts in Dynamo, let’s take a look at connecting Revit libraries into the scripting environment. Remember, we imported our Dynamo core nodes with the first three lines in the block of code below. To import the Revit nodes, Revit elements, and the Revit document manager, we only have to add a few more lines:
import clr clr.AddReference('ProtoGeometry') from Autodesk.DesignScript.Geometry import * # Import RevitNodes clr.AddReference("RevitNodes") import Revit # Import Revit elements from Revit.Elements import * # Import DocumentManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager import System
This gives us access to the Revit API and offers custom scripting for any Revit task. By combining the process of visual programming with Revit API scripting, collaboration and tool development improve significantly. For example, a BIM manager and a schematic designer can work together on the same graph. In this collaboration, they can improve design and execution of the model.
Platform Specific APIs
The plan behind the Dynamo Project is to widen the scope of platform implementation. As Dynamo adds more programs to the docket, users will gain access to platform-specific APIs from the Python scripting environment. While Revit is the case study for this section, we can anticipate more chapters in the future which offer comprehensive tutorials on scripting in other platforms. Additionally, there are many IronPython libraries accessible now which can be imported into Dynamo!
The examples below demonstrate ways to implement Revit-specific operations from Dynamo using Python. For a more detailed review on Python’s relationship to Dynamo and Revit, refer to the Dynamo Wiki page. Another useful resource for Python and Revit is the Revit Python Shell Project.
Exercise 01
Create a new Revit Project. Download the example file that accompanies this exercise (Right click and «Save Link As. «). A full list of example files can be found in the Appendix. Revit-Doc.dyn
In these exercises, we’ll explore elementary Python scripts in Dynamo for Revit. The exercise will focus on dealing with Revit files and elements, as well as the communication between Revit and Dynamo.
This is a cut and dry method for retrieving the doc, uiapp, and app of the Revit file linked to your Dynamo sesson. Programmers who have worked in the Revit API before may notice the items in the watch list. If these items do not look familiar, that’s okay; we’ll be using other examples in the exercises below.
Here is how we’re importing Revit Services and retrieving the document data in Dynamo:
A look at the Python node in Dynamo. The commented code is below.
# Enable Python support and load DesignScript library import clr # Import DocumentManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager # Place your code below this line doc = DocumentManager.Instance.CurrentDBDocument uiapp = DocumentManager.Instance.CurrentUIApplication app = uiapp.Application # Assign your output to the OUT variable. OUT = [doc,uiapp,app]
Exercise 02
Download the example files that accompanies this exercise (Right click and «Save Link As. «). A full list of example files can be found in the Appendix. Revit-ReferenceCurve.dyn
In this exercise, we’ll make a simple Model Curve in Revit using the Dynamo Python node.
- Create a code block and give it a value of «0;».
- Plug this value into a ReferencePoint.ByCoordinates node for X,Y, and Z inputs.
- Create three sliders, ranging from -100 to 100 with a step size of 1.
- Connect each slider to a ReferencePoint.ByCoordinates node.
- Add a Python node to the workspace, click the «+» button on the node to add another input and plug the two references points into each input. Open the Python node.
- System.Array: Revit needs a System Array as an input (rather than a Python list). This is just one more line of code, but paying attention to argument types will facilitate Python programming in Revit.
import clr # Import RevitNodes clr.AddReference("RevitNodes") import Revit # Import Revit elements from Revit.Elements import * import System #define inputs startRefPt = IN[0] endRefPt = IN[1] #define system array to match with required inputs refPtArray = System.Array[ReferencePoint]([startRefPt, endRefPt]) #create curve by reference points in Revit OUT = CurveByPoints.ByReferencePoints(refPtArray)
From Dynamo, we’ve created two reference points with a line connecting them using Python. Let’s take this a little further in the next exercise.
Exercise 03
Download and unzip the example files that accompany this exercise (Right click and «Save Link As. «). A full list of example files can be found in the Appendix. Revit-StructuralFraming.zip
This exercise keeps it simple, but drives home the topics of connecting data and geometry from Revit to Dynamo and back. Let’s begin by opening Revit-StructuralFraming.rvt. Once opened, load Dynamo and open the file Revit-StructuralFraming.dyn.
This Revit file is about as basic as it gets. Two reference curves: one drawn on Level 1 and the other drawn on Level 2. We want to get these curves into Dynamo and maintain a live link.
- Select Model Element Nodes: Hit the select button for each and select a corresponding curve in Revit.
- Code Block: using the syntax «0..1..#x;», connect an integer slider ranging from 0 to 20 into the x input. This designates the number of beams to draw between the two curves.
- Structural Framing Types: We’ll choose the default W12x26 beam here from the dropdown menu.
- Levels: select «Level 1».
This code in Python is a little more dense, but the comments within the code describe what’s happening in the process:
import clr #import Dynamo Geometry clr.AddReference('ProtoGeometry') from Autodesk.DesignScript.Geometry import * # Import RevitNodes clr.AddReference("RevitNodes") import Revit # Import Revit elements from Revit.Elements import * import System #Query Revit elements and convert them to Dynamo Curves crvA=IN[0].Curves[0] crvB=IN[1].Curves[0] #Define input Parameters framingType=IN[3] designLevel=IN[4] #Define "out" as a list OUT=[] for val in IN[2]: #Define Dynamo Points on each curve ptA=Curve.PointAtParameter(crvA,val) ptB=Curve.PointAtParameter(crvB,val) #Create Dynamo line beamCrv=Line.ByStartPointEndPoint(ptA,ptB) #create Revit Element from Dynamo Curves beam = StructuralFraming.BeamByCurve(beamCrv,designLevel,framingType) #convert Revit Element into list of Dynamo Surfaces OUT.append(beam.Faces)
In Revit, we have an array of beams spanning the two curves as structural elements. Note: this isn’t a realistic example. the structural elements are used as an example for native Revit instances created from Dynamo. In Dynamo, we can see the results as well. The beams in the Watch3D node refer to the geometry queried from the Revit elements.
Notice that we have a continuous process of translating data from the Revit Environment to the Dynamo Environment. In summary, here’s how the process plays out:
- Select Revit element
- Convert Revit element to Dynamo Curve
- Divide Dynamo curve into a series of Dynamo points
- Use the Dynamo points between two curves to create Dynamo lines
- Create Revit beams by referencing Dynamo lines
- Output Dynamo surfaces by querying the geometry of Revit beams
This may sound a little heavy handed, but the script makes it as simple as editing the curve in Revit and re-running the solver (although you may have to delete the previous beams when doing so). This is due to the fact that we are placing beams in python, thus breaking the association that OOTB nodes have.
With an update to the reference curves in Revit, we get a new array of beams.
Revit API using Python — Example
Let’s extract the Wall location. We can get find the method using dir(walls[0]) or the Revit Lookup:
locCrvs = [] #Create an empty list to store the curves for w in walls: locCrvs.append(w.Location) #Assign your output to the OUT variable. OUT = locCrvs
If we try to convert the output to a Dynamo line using ToProtoType() we get an error. That’s because we need to extract the Curve from the LocationCurve first. This can be seen both from Revit Lookup and the dir(locCrvs[0]) output (there isn’t a ToProtoType() method and there is a Curve method). So if we run:
locCrvs = [] #Create an empty list to store the curves for w in walls: locCrvs.append(w.Location.Curve) #Assign your output to the OUT variable. OUT = locCrvs
The output is finally a Revit line:
We can use the ToProtoType() method to it into a Dynamo line:
locCrvs.append(w.Location.Curve.ToProtoType())
5. Create a line load
Let’s now see how can we create a line load at the bottom of the wall. First of all let’s check on revitapidocs which method should we use.
- The method is available from Revit 2016 and it has not been modified since, this can be seen at the top of the page.
- The class is LineLoad and it has 3 different ways to create a line load. We want to create a line load from a line so we pick the third one.
- This class is part of the Autodesk.Revit.DB.Structure namespace so we need to load that in our python script.
We can find a description of each parameter at the bottom of the page:
public static LineLoad Create( Document aDoc, XYZ startPoint, XYZ endPoint, XYZ forceVector, XYZ momentVector, LineLoadType symbol, SketchPlane plane )
We already have the Document so we can get the start and end point of our LineLoad. Let’s assume that we want them to be the same as the wall location curve. We can use RevitLookup to find a method that gives us the start and end points:
or we can use the dir() method inside the python script. This will show us that there is a method called GetEndPoint that we can use:
This will give us the start and end points:
for w in walls: locCrvs = w.Location.Curve stPt = locCrvs.GetEndPoint(0) endPt = locCrvs.GetEndPoint(1)
forceVector and momentVector are just points. We can create them using XYZ()
LineLoadType and Sketchplane can be set to null (None in python) to use the default values. Because we are modifying the Revit project we need to create a transaction. We can use the Dynamo Transaction framework:
TransactionManager.Instance.EnsureInTransaction(doc) for w in walls: . TransactionManager.Instance.TransactionTaskDone()
or the Revit API one, which allows us to give a name to the transaction (which will be shown in the undo list):
transaction = Transaction(doc) transaction.Start("Create Line Loads") for w in walls: . transaction.Commit()
Last thing, the LineLoad Class is part of the Autodesk.Revit.DB.Structure namespace (shown at the top of the revitapidocs page). Therefore we need to add that to our script:
clr.AddReference("RevitAPI") from Autodesk.Revit.DB import * from Autodesk.Revit.DB.Structure import LineLoad
The completed script will be:
import clr clr.AddReference("ProtoGeometry") from Autodesk.DesignScript.Geometry import * # Import RevitAPI clr.AddReference("RevitAPI") from Autodesk.Revit.DB import * from Autodesk.Revit.DB.Structure import LineLoad clr.AddReference("RevitAPIUI") from Autodesk.Revit.UI import TaskDialog # Import DocumentManager and TransactionManager clr.AddReference("RevitServices") from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager # Import ToProtoType, ToRevitType geometry conversion extension methods clr.AddReference("RevitNodes") import Revit clr.ImportExtensions(Revit.GeometryConversion) doc = DocumentManager.Instance.CurrentDBDocument uiapp = DocumentManager.Instance.CurrentUIApplication app = uiapp.Application walls = FilteredElementCollector(doc).OfClass(Wall).ToElements() locCrvs = [] #Create an empty list to store the curves lLoad = [] transaction = Transaction(doc) transaction.Start("Create Line Loads") #TransactionManager.Instance.EnsureInTransaction(doc,"Create Line Loads") for w in walls: locCrvs = w.Location.Curve stPt = locCrvs.GetEndPoint(0) endPt = locCrvs.GetEndPoint(1) lLoad.append(LineLoad.Create(doc, stPt, endPt, XYZ(0,0,-10000), XYZ(0,5000,0), None, None)) #TransactionManager.Instance.TransactionTaskDone() transaction.Commit() #Assign your output to the OUT variable. OUT = lLoad