It has been a while since I last posted and that’s because I’ve recently started consulting for the splendid architectural company of Glenn Howells Architects. As I am trying to help their awesomely cute young architectural staff overcome the initial hurdles of taking up Revit, I enjoy the dynamics of travelling between their Birmingham and London offices.

What has taken the most part of my working hours lately, however, has been my recent recruitment with BIG. The BIG, as in Bjarke Ingles Group BIG. A studio that I’ve always admired both for their visionary architecture and for the unique image that they have managed to coin for themselves – that of a rebellious, beer drinking, bearded intellectual. Or maybe that’s just Bjarke Ingles himself, I’m not too sure.

Anyways, those and other (successful) projects have been taking a lot of my time, but they have also given me the opportunity to build some new, handy Dynamo definitions that I will now share with you. As you can imagine from the title of the post, I am going to show you 3 separate definitions, all of them using Python script extensively to achieve their tasks.

Batch Sheet Creation

“This should be in by default!” you sometimes exclaim when working with Revit, am I right? And it should. It annoys the hell out of me that Revit still doesn’t give you those simple, trivial even functionalities that end up wasting if not the majority than at least some of your production time.

The graph is pretty simple, as I said everything is inside the Python node.

Inside the python node, we will first see all the import statements. We need certain libraries and in most cases, those will be quite similar, that’s why I have a separate image which I will refer to when speaking about the other two scripts. One thing that I find myself doing over and over again is copying these statements from an old python node when I start working on a new one. You might find it handy to keep a default python script node stored with your favorite presets.

After we have that covered, we can start writing the specifics of our script. The node will take 2 user parameters – the number of ‘copies’ and the desired sheet number pattern (this is something you might want to change outside as well as inside the python script if your project sheet numbering follows a different pattern style). To complement the ‘duplication’ nature of the definition, we need to be working within an active sheet view which parameters we will copy. The bulk of the code is executed inside an ‘if’ statement, which makes sure we are inside such a view.

Next, we will do some basic string manipulation to make sure we create a new sheet sequence with appropriate numbering. The following piece of code is taken from a node written by Dimiter Venkov (respect) which basically retrieves all elements in the view. What we need to get is the TitleBlock from the active view sheet (which automatically means that if we don’t have a TitleBlock on the sheet, we will get an error).   Of course, we can always replace this with another input to the python node which feeds an existing in the document TitleBlock.

Once that’s done, we are ready to create as many sheets as we want. In a new Transaction, we iterate over the number of sheets we have chosen to make and we increment the sheet_number each time to make sure we create those unique sheet numbers.

That’s it! You are free to change anything around that code to make it fit the different scenarios you have in mind.

Duplicate Views and Place on Sheets

Here, we will still be creating new sheets, but this time, the focus is on duplicating a number of views first and then placing them on new sheets. This is yet another example of something that we wish we were given out-of-the-box and something that made a lot of people out there create custom macros, plugins, and dynamo definitions in an attempt to patch that Revit hole (I’ve spoken about the View Duplicatorplugin in one of my previous posts). This python script is just another take on the same subject. Let’s take a look.

This time, the sheet creation part is externalized and handled by the Sheet.ByNameNumberTitleBlockAndView node. You can find this lengthy title under the Revit/Views/Sheet tab. It takes a couple of parameters (you can see how we generate the TitleBlock this time) and the one we care about the most is the View or the list of Views that we would like to duplicate. This is done inside the python node, which itself takes 5 string parameters – a Prefix and a Suffix of the views that we are looking for, a Prefix and a Suffix with which we are going to replace the old ones (this gives you control over how the views will be named) and a string that will supply our sheets with unique numbers.

The so-called ‘Sheet Sequence’ code block has a little quirkiness that I’d like to explain – since normally we would be looking for a sequential sheet numbering, the string is of the format “xx-nn%02d”. What that means is that, once inside python, the sheet number will append the alphabetical part of the provided string, while incrementing the numerical one. Not only that but the numerical part, the way you can see it on the image, will be of the form “100” and it will increment to “101”, “102” and so on. The “02d” part after the “%” sign tells the formatted to treat whatever integer it is given as a double decimal number. So, if you would like to replace that with a single number, you would change it to “xx-nn%01d”. Mini exercise – if you want your sheet sequence to be “SK-3020”,  “SK-3021” … you will have to put? That’s right, “SK-302%01d”. As we will not be selecting the views directly from the project, we would like to be able to find them from all the other existing views. In a suitably maintained project, your view names will follow a certain pattern and this will make a lot of sense. If, for some reason, this method of selecting views does not work for you, feel free to change the python node to your liking.

As we agreed, all the import statements are the same as the ones we had before, so we can just skip that part and go straight to the core of the node. We are retrieving all views in the project and we match the ones that have the Prefix and Suffix strings in their names (hint – if those strings are left blank “”, this code will obviously match every single view in the project). There is one more requirement that you need to be aware of:

v.ViewType == ViewType.FloorPlan

This will only check our FloorPlan views. We can certainly change that when we are looking for CeilingPlans, AreaPlans and so on. For more information on the correct names of all view types, you can refer to the View.ViewType Property in the Revit Api documentation.

The rest of the code is pretty straight forward to follow, with the only exception of how we create the duplicate view and change access its properties.

v_duplicate = doc.GetElement(v.Duplicate(ViewDuplicateOption.Duplicate))

v_duplicate.ViewName = new_name

There are few things happening in that first line. First of all, we create a new view with the following piece of the code: v.Duplicate(ViewDuplicateOption.Duplicate). There are three options that we can use – .Duplicate, .AsDependent, .WithDetailing. I will leave cracking this complicated code to you. Then we get an object (v_duplicate) by retrieving the newly create view from the document via doc.GetElement. Finally, we can manipulate that object and assign our desirable name to it.

There are lots of ways to break the above code and that is simply because of the many different ways to define the task, especially in the string manipulation part. As always, this is meant to be a guideline and a source of python code that you can string together in a fashion that will do your bidding.

Workset Tidy-Up

Our final definition deals with the tedious task of cleaning up worksets. I don’t think that the problem needs explanation and I am sure that we are all familiar with the fact that our project files need maintenance. The definition that I will show you works especially well in a scenario where Worksets attribution follows the Revit Categories, but even in a non-standard setup it can still be modified in such a way as to allow you to automate at least part of the whole process.

I believe that the graph is easy to read – we get elements by Categories and check if their Workset parameter is not equal to the desired one. If it’s not, then we will assign the correct one. Of course, we can simply override the Workset parameter blindly, but that will take more time while the above solution will be snappy and quick the more we use it. It also has embedded the resources to make a more complex solution that filters elements by some other input. Finally, if the setup of your project is robust enough, you can batch collect categories by name and funnel those into their respected Worksets, potentially creating a 1 button solution for the whole problem.

As it turns out, this definition does not have any Python code in it. I got confused, as my usual workflow in Dynamo inevitably involves Python scripting. I would like to say a few words about that and I think that this is as good place as any for my little Grasshopper/Dynamo tirade that has been building inside for a while now.

“Dynamo is Grasshopper for Revit”. How many times have we heard this line? I bet a lot. And while Dynamo seems to have started as a Grasshopper for Revit and while there are certainly enough people out there that drive steadily in that direction, I personally think that because of the underlying differences between the two core programs that those “add-ons” serve – Rhino and Revit – there will and should always be a difference between GH and Dynamo. Rhino deals with complex geometry in a simple way. Revit deals with simple building components in a complex and granular way. Grasshopper has a clean, well-defined interface that intuits the way you deal with it. It is hard to say that Dynamo has any interface at all. It is ugly, awkward, has tons of outdated, free components that no one knows how to use but the builders themselves (there is no certainty in that either) and it forces its users to deal with scripting first hand. And you know what? I think that’s great! I think that scripting is not so hard and that Dynamo will eventually create a more knowledgeable and intelligent community around itself than the cry-baby generation of Rhino geometry junkies. By the way, I don’t mean that. I love Rhino and Grasshopper, I do, I do, I do. But that’s exactly why we should leave complex geometry to Rhino and embrace the documentation awesomeness of Revit and bring it to the next level with our Python augmented Dynamo super powers!

Peace out.

Oh, and you can download the three definitions here: Batch Sheet CreationDuplicate Views and Place on SheetsWorksets Tidy-Up.

When managing projects of non-small sizes, one of the tedious routines that you are required to go through is to somehow match information that is part of the project’s Sheet set and its View set. The idea of cross-referencing data between various elements is native for information management, yet Revit does not yet cover that aspect for us. Dynamo, however, does (so, in a sense, Revit does. Duh).

Traditionally, we could approach that task as follows:


  • load all sheets
  • load all views
  • for each sheet and view, see which view the sheet is sat on and finally
  • check if the corresponding parameters match and if not
  • do something (make them equal)


Now, there is one little bug that has been migrating with every update of Dynamo, and the brand new 1.0.0 (Congratulations!) version still apparently still got it – when retrieving elements by Category, the View Category will spit a message which states that Template Views are not Views. That’s fine. There is an even nicer node that simplifies the task for us:


What this node is supposed to do is to only select those Views that are place on the set of Sheets it is given. Now we can skip a step, retrieve only the Views that matter, and continue with our cross-referencing. .. but this node ,too, does not work.

That’s also fine. Here is the solution to the problem, written in python code.

.. and here is what the definition looks like:

What we did here is that we collected all the sheets in the project, using the Dynamo node of retrieving All Elements by Category, then we fed the resulting list together with a couple of shared parameters, which both the Sheet and the Views of the project use, and we executed the python code which, on one hand matched the information of those shared parameters, and on the other, gave us a handy list of ‘failed’ views. Those ‘failed’ views are simply the views that had View Templates assigned on, controlling and thus ‘locking’ the shared parameters.

While we are on the subject of python and getting instance parameters from elements, I want to point out the correct (at least for now) way of doing it.

The general syntax of this command in Python is:

element.Parameter[“Parameter Name”].AsString() – to get (can also be .AsInteger(), .AsDouble()) the parameter, or

element.Parameter[“Parameter Name”].Set – to set the parameter.

This is important to know as a lot of times Dynamo tasks revolve around Parameter manipulation and there is hardly any information out there that explains how this can be achieved in Dynamo with Python.

I hope that custom Dynamo definition can be of help to you in your BIM journeys my friends! Good night and best of luck.