anatomy of the dynamo python node

Before you begin...., WARNING. This digs into each word and meaning in hopes that remembering things by their process will help retain whats learned. If a placeholder ( a word you do not truly understand the function and role of in the macro process) will not allow you to work through the logic and reconstruct the memory of how to. So with great tedium let us begin.

Dynamo

Dynamo is a Microsoft .NET application that runs in a Microsoft Virtual Machine (VM).

https://dynamobim.org/

https://developer.dynamobim.org/

https://github.com/DynamoDS

Dynamo API Documentation = http://dynamods.github.io/DynamoAPI/

Python vs IronPython

The only difference is that IronPython was designed to run on the CLR (.NET Framework), and as such, can inter-operate and consume .NET assemblies written in other .NET languages. So if your platform is Windows and you also use .NET or your company does then should consider IronPython.


Python

  • written in C
  • batteries included with lot of libraries
  • latest and greatest Python features
  • Less multi-core support
  • Does not use CLR

IronPython

  • written in C#
  • some compatible batteries included with some of the C Python libraries + MS .NET libraries
  • Feature lag: IronPython necessarily lags behind C Python as there is more development focus for C Python
  • Better concurrency/multi-core support, due to lack of a GIL. (Note that the GIL doesn't inhibit threading on a single-core machine---it only limits performance on multi-core machines.)
  • Uses CLR
  • Limited ability to consume Python C extensions. The Ironclad project is making significant strides toward improving this---they've nearly gotten Numpy working!


Here is a link to another confused individual like me that seems to be slightly ahead of where I am. However the answer is still over my head. https://stackoverflow.com/questions/49082182/what-it-means-ironpython-is-an-implementation-of-the-python-programming-langu

Python vs IronPython

The only difference is that IronPython was designed to run on the CLR (.NET Framework), and as such, can inter-operate and consume .NET assemblies written in other .NET languages. So if your platform is Windows and you also use .NET or your company does then should consider IronPython.


Python

  • written in C
  • batteries included with lot of libraries
  • latest and greatest Python features
  • Less multi-core support
  • Does not use CLR

IronPython

  • written in C#
  • some compatible batteries included with some of the C Python libraries + MS .NET libraries
  • Feature lag: IronPython necessarily lags behind C Python as there is more development focus for C Python
  • Better concurrency/multi-core support, due to lack of a GIL. (Note that the GIL doesn't inhibit threading on a single-core machine---it only limits performance on multi-core machines.)
  • Uses CLR
  • Limited ability to consume Python C extensions. The Ironclad project is making significant strides toward improving this---they've nearly gotten Numpy working!


Here is a link to another confused individual like me that seems to be slightly ahead of where I am. However the answer is still over my head. https://stackoverflow.com/questions/49082182/what-it-means-ironpython-is-an-implementation-of-the-python-programming-langu

Example to break down

#ModifyDeleteElements#Sample on how to delete model elements.
import clr
# Import Revit Services
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
# get the current Revit document
doc = DocumentManager.Instance.CurrentDBDocument
# Dynamo inputs
elements = UnwrapElement(IN[0])
# start Revit transaction
TransactionManager.Instance.EnsureInTransaction(doc)
# loop over each element to delete.
for e in elements:
doc.Delete(e.Id)
# finish transcation in Revit.
TransactionManager.Instance.TransactionTaskDone()
OUT = "done"


Import

CLR

CLR stands for "Common Language Runtime" (CLR) execution environment

Microsoft here after referred to as "MS" has created a middle man to convert non CLI specification standard code to MS Machine code (aka native code). This takes multiple steps in the .NET Framework. MS at the time it is needed creates a VM or Virtual Machine that manages this process.

Step 0

You like that don't you... i started my list with zero. I love this programming stuff. If Back to it. In Python you run the code starting a chain reaction.

Step 1

Non Microsoft programming languages like Iron Python are complied when you run them. They are converted into Bytecode. Bytecode is program code that has been compiled from source code into lower-level code designed for a software interpreter.

Step 2

Create a VM and initialize the Virtual Execution System (VES)

Step 3

CLR is the interpreter. CLR translates/ converts the Bytecode into assembly code. This is done with Just-in-time compilation which converts the managed code (compiled intermediate language code), into machine instructions which are then executed on the processor.

Just-in-time or JIT is used because programming must be built for a specific processor architecture or at the last possible second check to see which processor chip is going to be used during this run and Just In time convert the managed code to work with the specific processor.

Step 4

The reason there is a CLI & CLR intermediate language is that every computer has has a different possible Processor chip (hardware) built to work in a specific proprietary way, this is called processor architecture. Each processor architecture requires instructions in a different way that works with the architecture. CLR is a open source standard way of getting from most computer languages like Python and others to an agreed upon standard (CLI) that can talk to the processor chips customized assembly language. Then an utility tool called an assembler does a finial conversion to the processors proprietary Machine code (a computer programming language consisting of binary or hexadecimal instructions which a computer can respond to directly, aka native code).

Step 5

Execute

Step 6

Close VM, results (Done)


The path to the installation directory of the CLR can be in multiple location depending on 32bit (x86) or 64bit (x64) systems as well as .NET version. The CLR can either be loaded from C:\Windows\Microsoft.NET\Framework64\v2.0.50727

or from

C:\Windows\Microsoft.NET\Framework\v2.0.50727

Why define these terms? The CLR is often refereed to as a module. I feel this is likely incorrect in the understanding that CLR is part of an assembly. In the broader sense it is a sub component of the .NET framework.

A module is an .exe or .dll file. An assembly is a set of one or more modules that together make up an application. The term "module" is often used synonymously to mean different things to different people. After looking, a hard definition relative to programming could not be found, leading to the word being confusing. The best we can do is to say a module is a component of an assembly.

reference link

clr.AddReference("RevitServices")

Image below is the RevitServices.dll decomplied

AddReference is a method defined in the CLR execution environment to load in assemblies (also called libraries, like .dll or dynamic link library).

RevitServices = C:\Program Files\Dynamo\Dynamo Revit\2\Revit_2019\RevitServices.dll

I am not able to find more information so in order to see inside the RevitServices.dll you have to decompile it. Instruction here

from RevitServices.Persistence import DocumentManager

Persistence, in computer science, is a noun describing data that outlives the process that created it. Persistence here is likely used in the idea that the Document Manager looks to an existing DataBase known as the .rvt document we store our models in. As well this .rvt DataBase outlives a single session of Revit and or this DataBase query.

from RevitServices.Transactions import TransactionManager

Transaction. Any change to a document can only be made while there is an active transaction open for that document. Changes do not become part of the document until the active transaction is committed. Consequently, all changes made in a transaction can be rolled back either explicitly or implicitly by the transaction's destructor.

A document can have only one transaction open at any given time.

Transactions cannot be started when the document is in read-only mode, either permanently or temporarily. See the Document class methods IsReadOnly and IsModifiable for more details.

Transactions in linked documents are not permitted, for linked documents are not allowed to be modified.

If a transaction was started and not finished yet by the time the Transaction object is about to be disposed, the default destructor will roll it back automatically, thus all changes made to the document while this transaction was open will be discarded. It is not recommended to rely on this default behavior though. Instead, it is advised to always call either Commit or RollBack explicitly before the transaction object gets disposed. Please note that unless invoked explicitly the actual destruction of an object in managed code might not happen until the object is collected by the garbage collector.

-citation, C:\Revit 2019.2 SDK\RevitAPI.chm, search Transaction ClassRevit 2019 SDK (software Development Kit)

Transactions

Dynamo provides its own Transaction framework for working with the RevitAPI. This means that your Python script will be executing in the context of an overall Dynamo Transaction.

If you are writing RevitAPI code that requires a Transaction, then you may use the Dynamo TransactionManager.

  • TransactionManager.EnsureInTransaction(): Initializes the Dynamo Transaction
  • TransactionManager.TransactionTaskDone(): Tells Dynamo that we are finished using the Transaction
  • TransactionManager.ForceCloseTransaction(): Tells Dynamo to commit the active Transaction. This is slower than TransactionTaskDone(), so only use it when you actually need to close the Transaction for your script to work.

For your scripting, any place where you would normally create a new RevitAPI Transaction and then call Transaction.Start(), you will instead use TransactionManager.EnsureInTransaction(). Any place where you would normally call Transaction.Commit(), you will instead use TransactionManager.TransactionTaskDone(). Any place where you actually definitely need the Transaction to end (sitations where you want to operate on the model after regeneration occurs), then you may use TransactionManager.ForceCloseTransaction().

import clr

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

# Get the document
doc = DocumentManager.Instance.CurrentDBDocument

# "Start" the transaction
TransactionManager.Instance.EnsureInTransaction(doc)

# Create a reference point (requires a transaction)
refPt = doc.FamilyCreate.NewReferencePoint(XYZ(0, 0, 0))

# "End" the transaction
TransactionManager.Instance.TransactionTaskDone()

You may also use RevitAPI Sub-Transactions alongside (but not as a substitute for) the TransactionManager. Sub-Transactions will give you the ability to rollback your changes. There is no way to rollback the main Dynamo Transaction

DocumentManager.Instance.CurrentDBDocument

doc = is a declaring a variable "doc" who's value can change. The value in this case is the current (open and active) document who's name is retrieved via the Document Manager in the RevitServices.dll

elements = UnwrapElement(IN[0])

What UnwrapElement() does and what it means. UnwrapElement in IronPython allows access to the element directly in the Revit API, bypassing the Python interpreter (which lacks some of the Revit API functionality). Below is a deeper dive.

Example:

customvariablename = UnwrapElement( IN[0] )

do something

OUT = customvariablename.ToDSType(false)

RevitAPI

To facilitate easier interaction between the RevitAPI and Dynamo, we have written a comprehensive library for interacting with Revit. This means that all of our Revit data types passed between Dynamo nodes are actually wrappers around the core data types; these wrappers help Dynamo remain in sync with Revit. These changes will affect old Python scripts written for 0.6.3.

Elements

All Elements coming out of Dynamo Nodes are actually wrappers around core Revit Elements. Inside of Python, you can operate on these types directly by calling our nodes from inside of Python, which are all located in the Revit.Elements namespace:

import clr

# Import RevitNodes
clr.AddReference("RevitNodes")
import Revit
# Import types we need. Instead of listing individual types,
# you can do 'from Revit.Elements import *'
from Revit.Elements import CurveByPoints, ReferencePoint

import System

startRefPt = IN[0]
endRefPt = IN[1]
refPtArray = System.Array[ReferencePoint]([startRefPt, endRefPt])
OUT = CurveByPoints.ByReferencePoints(refPtArray)

If you would prefer to use the RevitAPI directly, you will need to unwrap the Element before operating on it, use our TransactionManager to ensure that you're operating inside of a RevitAPI Transaction, and wrap any Element you wish to return.

import clr

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import ReferencePointArray

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

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Unwrap
startRefPt = UnwrapElement( IN[0] )
endRefPt = UnwrapElement( IN[1] )

# Start Transaction
doc = DocumentManager.Instance.CurrentDBDocument
TransactionManager.Instance.EnsureInTransaction(doc)

# Make the CurveByPoints
arr = ReferencePointArray()
arr.Append(startRefPt)
arr.Append(endRefPt)
cbp = doc.FamilyCreate.NewCurveByPoints(arr)

# End Transaction
TransactionManager.Instance.TransactionTaskDone()

# Wrap
OUT = cbp.ToDSType(false)

Unwrapping

Wrapped Elements are located in the Revit.Elements namespace. All wrapped Elements extend the Revit.Elements.Element abstract class. This class provides a public property InternalElement which contains a reference to the underlying RevitAPI Element, of type Autodesk.Revit.DB.Element. Alternatively, Dynamo provides a convenience function UnwrapElement(element) function that accepts wrapped Elements or arbitrarily nested lists of wrapped Elements. If passed a non-Element it will simply return the object unmodified.

wrappedElement = IN[0]
unwrappedElement = UnwrapElement( wrappedElement )
# Now I can use 'unwrappedElement' with the RevitAPI

Wrapping

In order to interoperate with our Revit nodes, any raw Autodesk.Revit.DB.Element being returned from a Python script must be wrapped in a Revit.Elements.Element. This can be done by using the ToDSType(bool) extension method. The bool argument determines whether or not the Element is "Revit-owned." This distinction is important: Revit-owned Elements are not controlled by Dynamo, non-Revit-owned Elements are. Basically, if you are creating a new Element in your Python script, then you should not mark the wrapped Element as Revit-owned. If you are selecting an Element from the Document, then you should mark the wrapped Element as Revit-owned.

import clr

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

docPt = FetchRefPtFromDoc() #Fetch an existing ref pt (not a real method)
newPt = CreateNewRefPt()    #Create a new ref pt (not a real method)
OUT = [ 
    docPt.ToDSType(True), #Not created in script, mark as Revit-owned
    newPt.ToDSType(False) #Created in script, mark as non-Revit-owned
]

TransactionManager.Instance.EnsureInTransaction(doc)

For Transaction Manager reference the above section titled "from RevitServices.Transactions import TransactionManager"


TransactionManager.EnsureInTransaction(): Initializes/ starts the Dynamo Transaction

This is the first part of the process to edit a database (your revit file is the database)

Loop

for e in elements:
doc.Delete(e.Id)

for = loop or run an action on a specified number of times equal to the number of elements in this instance.

e = element here but could be anything (The for loop does not require an indexing variable to set beforehand. )

in =

elements: = a list supplied to the Input ( IN[0] ) or another input possibly

doc.Delete(e.Id) = https://www.revitapidocs.com/2015/a0461dd1-71d9-4581-1604-2ef8c211dd60.htm

doc.Delete(ElementId(element.Id))

i do not understand how this code works with out https://forum.dynamobim.com/t/deleting-elements-using-python/15064/6?u=dbrokaw


www.w3schools.com/python/python_for_loops.asp

https://stackoverflow.com/questions/522563/accessing-the-index-in-for-loops

https://wiki.python.org/moin/ForLoop

TransactionManager.Instance.TransactionTaskDone()

TransactionManager.Instance.TransactionTaskDone()
OUT = "done"

For Transaction Manager reference the above section titled "from RevitServices.Transactions import TransactionManager"


TransactionManager.TransactionTaskDone(): Tells Dynamo that we are finished using the Transaction

This is the last part of the process to edit a database (your revit file is the database)

Links