I have been working for the past year on a very large hotel complex that consists of five hotel towers between 21 and 40 stories tall.  Recently, as the project entered the DD phase I was tasked with the challenge of intelligently numbering all 5620 rooms in the project according to the scheme devised by our signage consultant.

So conceptually there are a couple major issues to confront.  First I will need to tie together information from the Tower location, level and room into a single number to be assigned to the room number parameter.  The more difficult issue is how to ensure that rooms stack from level to level while skipping back of house rooms and elevators in the naming scheme.  Additionally, on the upper levels there are suites which occupy between two and six 'typical' guestroom slots but should only have a single number.  

Process

Conceptually it works by defining a series of correctly numbered Revit areas and then running through every room in the project and matching it to its corresponding area.  Our graphics consultant provided us the following numbering guide which needed to be iterated over all 5620 keys in the project.

 

Next I needed to create the areas the script would reference when searching for the appropriate number.  Dynamo will assign an XYZ coordinate location to each area based on the 'reference mark' (this can be turned on in visibility properties).  We only need to worry about the reference location so areas do not need to be bounded or named.  They only have to have the correct location and Number.  For simplicity I created the area plan at the lowest tower level - I'll be using Tower D as an example.

areaplan

These areas can be numbered by hand.  To save time I created a mini script that numbers them based on the knowledge that Revit assigns Revit IDs the elements in the order that they are created.  If these are the only areas in the project and they are created in order when called into Dynamo they will retain that order.  I went through Tower D and click the areas in order based on the graphics package.  This step can quite easily be done manually by simply renumbering the areas.

Now that we have everything set up we are ready to run the numbering script.  A typical guestroom tower as 3 room types for the purpose of this script.  We have the typical guestroom, the 2-bay guestroom suite and everything else.  We can differentiate between these types by using each rooms department parameter.

the script

This is an overview of how the overall script works.  i will walk through each section step-by-step.

Point Cloud

The first step is to create the point cloud of area names with corresponding XYZ locations to test.  Grabbing all of the areas we create a list of their XYZ locations and a corresponding list of their names.  We want to test these coordinates at every level so we grab all the levels in the project and their elevations.  We then take the XY coordinates and cross reference it with the list of elevations to create an array of points.  The output is a list [45 elements] of area names.  This list corresponds to the point cloud we've created which is in the format [45 lists each with 42 points] (see GUT CHECK).  List [0] of the point cloud represent area [0] in the name list.  The 42 points in list [0] represent each of the 42 levels in the project.  The end result is 42 XYZ points, that particular area on every level of the project.  We end up with a point cloud of 45 areas x 42 levels = 1890 points.

Next we are going to need all of the rooms in the project in order to test them against these points.  Using the room element collector we sort out all of the rooms with the department "Guestrooms".  Although these appear to be in order in this case it is not certain and we want to make sure all of the rooms are sorted by level.  Using the SortByKeyList function we can make sure that these room elements are sorted in order of level.  We end up with two corresponding output lists, each with all [1360] rooms and elevations in an ordered list.

The main body of this script is contained in two custom Python nodes which iterate simple for loops.  The first sorts all of the rooms from the last step into an array organized by level.  It takes in the following inputs: IN[0] - The elevation of all levels in the project after the first tower level [42] . IN[1] - Sorted list of room elevations [1360] IN[2] - Sorted list of room elements that correspond to the elevations [1360]

DO NOT BE INTIMIDATED BY CODE!  This is a simple for loop.  In the first block we assign each of our input lists to a variable.  The next block we create two empty lists output and subout to hold our values once we sort them.  

  • The first for loop finds the range of the list levels (IN[0]) which is 42 and then performs an operation for each level.
  • For each level it frist clears the list subout
  • Then for each level it performs a check for elevation in roomlevels (IN[1]) which has 1360 elements.
  • For each level it checks if the roomlevel is equal to the level
  • If these values are equal the room element from rooms (IN[2]) is added to the subout list.
  • At the end of every level the subout list is added to output before the loop returns to the start to check the next level.

code

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
#^^This is all standard inputs that can be ignored

#The inputs to this node will be stored as a list in the IN variable.

levels = IN[0]
roomlevels = IN[1]
rooms = IN[2]

output = []
subout = []

for l in range(len(levels)):
    subout = []
    for r in range(len(roomlevels)):
        if levels[l] == roomlevels[r]:
            subout.append(rooms[r])
    
    output.append(subout)
    
#Assign your output to the OUT variable
OUT = output
Python Sort Rooms.JPG

The output from this block of code is an array of sorted rooms with the organization [level][room] so that every index in [0] is a room element on level 09, every index in [1] is a room element on level 10, ect...

The second custom code block will operate on the sorted room list and assign the appropriate number based on the reference areas we created earlier.

  • IN[0] - A sorted array in the form [levels][rooms] with a varying number of room elements per level.
  • IN[1] - An array of points in the form [area coordinate X,Y][level]
  • IN[2] - A list of area names which corresponds to the [area coordinate X,Y] list of IN[1]

The second block of code is slightly more complex and revolves around the RevitAPI method .IsPointInRoom(xyz)  We once again assign our inputs to variable and create empty output lists.  

  • The first for loop is indicating it will perform the actions for every level in the project and on each level it will create a blank subout list
  • The try command allows the script to not fail even if a level has no room elements - a blank list.
  • For each room in our sorted array of rooms per level we will perform the following
    • Unwrap the Revit element and store the raw room data in room
    • For each room iterate through all 45 areas
      • For each area in that list of 45 grab the XYZ coordinate that coorespons to the level we are currently testing.  This means that instead of testing all 1890 points in our point cloud every iteration we only need to test the 42 on our current level.
      • Each of those points we perform the method IsPointInRoom and if this is true we grab the name that corresponds to that level from our variable names
    • Each room will only evaluate this method true for a single point.
    • On each level we create a list in subout of numbers(names) which correspond to the list of rooms sorted by level
  • For each level we add this subout list to output before beginning the for loop again on the next level.

code

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

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

rooms = IN[0]
points = IN[1]
names = IN[2]

namesOut = []
output = []

for level in range(len(rooms)):
subOut = []

try:
for room in rooms[level]:

room = UnwrapElement(room)

for p in range(len(points)):

point = points[p][level].ToXyz()
if room.IsPointInRoom(point):

subOut.append(names[p])

output.append(subOut)
except:
output = "FAIL"

#Assign your output to the OUT variable
OUT = output

The hard part is over. WOOHOO!  We now have two corresponding lists.  The output from the first python block is in the form [level][room elements] and the output from the second is [level][area number] and these lists should match exactly.  Now we simply need to assemble the room number in the correct form [Tower Name] - [Level][Room Number] (Do you see how this matches the form of the lists we created).  We grab the level name for each room element and pad it to make it two digits.  We then append this to the tower name which we input manually.  This gives us a list of all the prefixes for room numbers and then we just mash this together with our list of area numbers.  Because our array of sorted rooms and our array of area numbers match we can flatten all the lists to a single sorted list and then SetParameterByName

We have now numbered all of the typical guestrooms.  If we jump back the start of the script where we filtered by departments we can now deal with all of the rooms which did no have a department guestroom.  We can now sort these remaing rooms by which has the department "Guestrooms - 2-bay Suite".  These rooms will go through the EXACT SAME PROCESS the only difference being a minor change to the area number assignment python script.  Because some 2 bay suites will evaluate true for multiple area numbers we need to ensure only one gets assigned to the room.  We add an extra test to the IsPointInRoom method that only lets this evaluate once per room.  Once a room name is assigned it cannot evaluate True again. (NOTE: The proper way to do this is with a while loop but I couldn't get it to work)

The final step is easy.  We don't want any room numbers to be blank so we sort the remaining rooms by level.  We then number them sequentially in an unordered list starting from the last guestroom number + 1.

And VOILA! We've numbered 5000+ guestrooms and all tower rooms in a semi-simple manner.  The real advantage is this script can adapt to changes in the project, additional levels, reconfigured rooms, ect. as long as the reference areas are correct.

We could also add a simple block of nodes to assign other parameters such as room occupancy, location and name.

Hopefully this walk-through shows some example of how to use Dynamo nodes as well as how to use simple Python for loops to simplify Dynamo scripts.

Please contact me if you have any questions.

Comment