Sum Apartment Rooms

Let's try to apply what we learnt about parameters to create a new Revit Add-In. We will create a plugin to sort rooms by apartment numbers and then sum them based on another parameter so we can display apartment sums in a tag.

Sum Apartment Rooms

Let's try to apply what we learnt about parameters to create a new Revit Add-In. We will create a plugin to sort rooms by apartment numbers and then sum them based on another parameter so we can display apartment sums in a tag.

Summary

Apply What Your Learn About Parameters

Let's put our skills to practice, shall we?

In this lesson I will show you how to create a simple but yet effective tool - Sum Apartment Rooms based on parameter value.

This will allow you to sort your rooms based on apartment number and then create sums based on living space types. I will keep it simple and only make 2 sums:

  • Living Space Sum (WNF)

  • Balcony Sum

In addition, you can adjust it to your real projects so you can create sums for Garden, Storage, Terrasses and whatever else you would like to display in a tag next to your apartments.

Brainstorming

Whenever you start creating a new add-in, try to break it down into smallest steps possible. You can do it on the paper or directly in pyCharm as comments.

The point is to create logical steps you can follow, one at the time.
It will also help you think it through. Often times you will discover that you need additional steps in between.

For this tool we will have to follow these steps:

  • 1️⃣ Get All Rooms

  • 2️⃣ Sort by Apartment Number Parameter

  • 3️⃣ Sum Rooms based on Another Parameter

  • 4️⃣ Write results to output parameters

These are fairly simple steps, so let's go through them and code it together!

1️⃣ Get All Rooms

First of all we need to get all rooms in the project.

We will have to use FilteredElementCollector Class for that. We haven't covered it yet in details, but there will be a whole module about it so you will learn everything about it in Module 06.

For now let's keep it simple:

#1️⃣ Get All Rooms
all_rooms = FilteredElementCollector(doc)\
              .OfCategory(BuiltInCategory.OST_Rooms)\
              .ToElements()
2️⃣ Sort Rooms by Apartments

Now we need to sort rooms based on an apartment number.

I will use parameter named 'Top' - It means apartment number in German.

In my case it's a parameter with a StorageType.String. If you use integers, you will also need to convert them to strings!


💡Also I will use defaultdict! It allows us to specify default value for new keys in dictionary.

Usually if you try to get a key that doesn't exist you get an error. But by specifying a default value, we don't have to worry about it!

So here is the snippet:

  • Choose Sorting Parameter

  • Create defaultdict

  • Iterate through rooms and read Top parameter

  • Add rooms to dict_rooms based on an apartment number

#2️⃣ Sort By Apartment Number
p_name_room_sorting = 'Top'

from collections import defaultdict
dict_rooms = defaultdict(list)

for room in all_rooms:
    try:
        top = room.LookupParameter(p_name_room_sorting).AsString()
        if top:
            dict_rooms[top].append(room)
    except:
        forms.alert("Couldn't Read 'Top' Parameter. Please Check and Try Again.", exitscript=True)

Before going to the next step, make sure you getting elements sorted correctly.
Just iterate through Key and Value of you dictionary by using .items() method.

In our case, Key will be an apartment number and Value will be a list of rooms.

#👀 Check Dictionary
for k,v in dict_rooms.items():
    print(k,v)

Here what I got in the console:

3️⃣ Sum Rooms based on Occupancy

Now we can start calculating apartment sums.
Keep in mind that we won't add all rooms in apartment in a single sum!

The goal here is to create multiple sums based on another parameter. In my case I use Built-In parameter - Occupancy, but you can use any parameter.

room_type = room.get_Parameter(BuiltInParameter.ROOM_OCCUPANCY).AsString()

💡Also remember - Revit API uses feet as internal units!

So we have to convert our units to calculate sums in metric.
We will also round them to 2 digits.

If you won't round them, you will have different sums because Revit stores more digits that it's shown on the screen and it might accumulate to different sum!

It's not fun to tell to your boss that we have a difference because of a rounding error, so make sure you double check your results.

I will use method from UnitUtils to ConvertFromInternalUnits.
This will work for Revit 2022+. You can use function for converting units that we made previously, to cover all Revit versions.

#🧮 Convert Area + Rounding
for room in rooms:
    room_type    = room.get_Parameter(BuiltInParameter.ROOM_OCCUPANCY).AsString()
    room_area_m2 = UnitUtils.ConvertFromInternalUnits(room.Area, UnitTypeId.SquareMeters)
    room_area_m2 = round(room_area_m2, 2)

Now we can create sums based on occupancy parameter.

The reason for multiple sums is simple. When we sell apartments we want to know how much m² of living, balcony, storage, garden and other sums we get.

And it will depend on the regulations and clients, so this script will allow us to be very flexible and adjust our calculations.

I will use 2 parameter values:

  • WNF (Living space in German)

  • Balkon

You will be able to apply the same principles to create more categories depending on your regulations!

Let's put it into a single snippet now:

#🔁 Iterate through dict_rooms
for top, rooms in dict_rooms.items():
    sum_wnf_m2    = 0
    sum_balkon_m2 = 0

    # 3️⃣ Sum Apartment Area
    for room in rooms:
        room_type    = room.get_Parameter(BuiltInParameter.ROOM_OCCUPANCY).AsString()
        
        #🧮 Convert Area + Rounding
        room_area_m2 = UnitUtils.ConvertFromInternalUnits(room.Area, UnitTypeId.SquareMeters)
        room_area_m2 = round(room_area_m2, 2)

        #👇 Sum based on Occupancy Parameter
        if room_type == 'WNF':
            # sum_wnf_ft += room.Area
            sum_wnf_m2 += room_area_m2

        elif room_type == 'Balkon':
            # sum_balkon_ft += room.Area
            sum_balkon_m2 += room_area_m2
        else:
            print('Wrong Value: {}'.format(room_type))

    #👀 Preview Results
    print('Apartment Number: {}'.format(top))
    print('SUM Living Space: {}'.format(sum_wnf_m2))
    print('SUM Balkon Space: {}'.format(sum_balkon_m2))
    print('---')

Now make sure you get correct results before going to the next step.

Here is my Print Statement:

4️⃣ Write results to Output Parameters

Lastly, we just need to write results to one of the parameters.

A few things to keep in mind:

  • Revit API uses feet!

  • We need Transaction to make any changes in our projects!

  • Make sure you use same StorageType as your parameter!

#📦 Output Parameter Names
p_name_wnf    = '[Σ] - WNF'		  #- Output Living Area
p_name_balkon = '[Σ] - Balkon' 	#- Output Balkon Area


#🧮 Converting M2 to FT
sum_wnf_ft    = UnitUtils.ConvertToInternalUnits(sum_wnf_m2,    UnitTypeId.SquareMeters)
sum_balkon_ft = UnitUtils.ConvertToInternalUnits(sum_balkon_m2, UnitTypeId.SquareMeters)


# 🔏 Start Transation
t = Transaction(doc, 'Apartment Sums')
t.Start() #🔓

#🎯 Write Results to Output Parameters
for room in rooms:
    try:
        p_wnf    = room.LookupParameter(p_name_wnf)
        p_balkon = room.LookupParameter(p_name_balkon)

        p_wnf.Set(sum_wnf_ft)
        p_balkon.Set(sum_balkon_ft)
    except:
        from pyrevit import forms
        forms.alert("Couldn't get Output parameters. Please check and try again", exitscript=True)

t.Commit() #🔒
✨ Final Code

Alright, we are done with this tool!

Here is the final code you can use to sum your rooms based on apartment number and its occupancy type!

💡 Make sure you adjust parameter names to your own projects!

💡 Ensure you convert units correctly if you use Revit older than 2022!

# -*- coding: utf-8 -*-
__title__ = "04.06 - Apartments Sum"
__doc__ = """Version = 1.0
Date    = 15.01.2024
_____________________________________________________________________
Description:
Apply Revit API Basics to create a tool.

The tool will calculate apartment room sums based on 2 parameters:
- Flat #
- Space Type (Living, Balkon...)

Then sums will be written in all rooms with the same flat number, 
so any room can be tagged to get this information on the plan.

Happy Coding!
_____________________________________________________________________
Author: Erik Frits"""

# ╦╔╦╗╔═╗╔═╗╦═╗╔╦╗╔═╗
# ║║║║╠═╝║ ║╠╦╝ ║ ╚═╗
# ╩╩ ╩╩  ╚═╝╩╚═ ╩ ╚═╝ IMPORTS
# ==================================================
from Autodesk.Revit.DB import *
from pyrevit import forms

# ╦  ╦╔═╗╦═╗╦╔═╗╔╗ ╦  ╔═╗╔═╗
# ╚╗╔╝╠═╣╠╦╝║╠═╣╠╩╗║  ║╣ ╚═╗
#  ╚╝ ╩ ╩╩╚═╩╩ ╩╚═╝╩═╝╚═╝╚═╝ VARIABLES
# ==================================================
doc   = __revit__.ActiveUIDocument.Document #type: Document
uidoc = __revit__.ActiveUIDocument
app   = __revit__.Application

# Global Variables
p_name_room_sorting = 'Top'

p_name_wnf    = '[Σ] - WNF'		#- Output Living Area
p_name_balkon = '[Σ] - Balkon' 	#- Output Balkon Area

# ╔╦╗╔═╗╦╔╗╔
# ║║║╠═╣║║║║
# ╩ ╩╩ ╩╩╝╚╝ MAIN
# ==================================================

#1️⃣ Get All Rooms
all_rooms = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).ToElements()

#2️⃣ Sort By Apartment Number
from collections import defaultdict
dict_rooms = defaultdict(list)

for room in all_rooms:
    try:
        top = room.LookupParameter(p_name_room_sorting).AsString()
        if top:
            dict_rooms[top].append(room)
    except:
        forms.alert("Couldn't Read 'Top' Parameter. Please Check and Try Again.", exitscript=True)

#👀 Check Dictionary
# for k,v in dict_rooms.items():
#     print(k,v)

#🔏 Start Transaction
t = Transaction(doc, 'Sum Apartments')
t.Start() #🔓

#🔁 Iterate through dict_rooms
for top, rooms in dict_rooms.items():
    sum_wnf_m2    = 0
    sum_balkon_m2 = 0

    # 3️⃣ Sum Apartment Area
    for room in rooms:
        room_type    = room.get_Parameter(BuiltInParameter.ROOM_OCCUPANCY).AsString()
        
        #🧮 Convert Area + Rounding
        room_area_m2 = UnitUtils.ConvertFromInternalUnits(room.Area, UnitTypeId.SquareMeters)
        room_area_m2 = round(room_area_m2, 2)

        #👇 Sum based on Occupancy Parameter
        if room_type == 'WNF':
            # sum_wnf_ft += room.Area
            sum_wnf_m2 += room_area_m2

        elif room_type == 'Balkon':
            # sum_balkon_ft += room.Area
            sum_balkon_m2 += room_area_m2
        else:
            print('Wrong Value: {}'.format(room_type))

    #👀 Preview Results
    print('Apartment Number: {}'.format(top))
    print('SUM Living Space: {}'.format(sum_wnf_m2))
    print('SUM Balkon Space: {}'.format(sum_balkon_m2))
    print('---')


    #🧮 Converting M2 to FT
    sum_wnf_ft    = UnitUtils.ConvertToInternalUnits(sum_wnf_m2,    UnitTypeId.SquareMeters)
    sum_balkon_ft = UnitUtils.ConvertToInternalUnits(sum_balkon_m2, UnitTypeId.SquareMeters)


    #4️⃣ Write Results to Output Parameter
    for room in rooms:
        try:
            p_wnf    = room.LookupParameter(p_name_wnf)
            p_balkon = room.LookupParameter(p_name_balkon)

            p_wnf.Set(sum_wnf_ft)
            p_balkon.Set(sum_balkon_ft)
        except:
            forms.alert("Couldn't get Output parameters. Please check and try again", exitscript=True)

t.Commit() #🔒

HomeWork

Now adjust that script to parameters used in your office and give it a spin! I am sure it will be useful to many of you working with rooms.

You can also improve the script by adding the following:

  • Use Reusable Function for Converting Units

  • Check If All Shared parameters are loaded before calculating

  • Clear output parameters from all rooms before calculation

  • Share the tool with your colleagues.

This will make the script more error-proof!

⌨️ Happy Coding!

Questions:

Why you didn't use reusable function for converting we made?

Why you didn't use reusable function for converting we made?

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API