Another Automation - Auto Combining Plenums

Keeping on with the automation trend, again with my workflow (maybe others also), I use Revit HVAC Zoning as a zonemap to know what spaces belong together for a thermal zone. This is the same zonemap I use for IESVE when having to move spaces to a zone and then combining the plenums (which is super slow).

The script from the plenum post used to autocreate the plenums, and somehow the zone parameter could be used to merge the plenums. Now the only thing is that when IESVE merges the plenums, it makes all the partitions that belong to the merged plenum and air boundary like in this example of 3 spaces in one zone

Below is the an attached .pomf with the user data.



2025-13-6–21-25-41_ECJF-HOK-ME-IESVE Heating_Cooling Loads.pomf (45.0 KB)

Hi @rafaelhok,

Let me make sure that I understand the goal here.

You would like the plenums of rooms within the same thermal zone to be merged together into a single plenum space with no partitions/air boundaries between them. Is that correct?

If that’s the case, this might be something that we automate differently than the other two since Pollination already has a concept of zoning, and we can add an option to the translation to merge the plenums for rooms on the same zone.

Let’s see what @chriswmackey thinks about this. I feel this is a common workflow and worth being an option in the standard export workflow.


Thanks again for post the automation requests! I’m very glad that you discovered this aspect of Pollination. It has been there for some time now, but we have been mostly using it for renaming rooms and moving models to the origin. These more advanced workflows make all the development worthwhile. :slight_smile:

“You would like the plenums of rooms within the same thermal zone to be merged together into a single plenum space with no partitions/air boundaries between them. Is that correct?”

Yes, exactly. This ideally makes the plenum merging instantaneous rather than manually merging.

Thanks, @rafaelhok .

To second what @mostapha said, I think we can probably support an automated workflow for this at some point once we expose the “Zone” attribute of Rooms on the model editor UI. In the meantime, there is a way to get what you want in the Model Editor, which is a little manual but should be far quicker than using IESVE to manage the plenum geometry.

What you can do is that, after you have set up all of your plenum depths correctly, you can use Separate Plenums button at the top of the “Levels” table:

This will convert the plenum properties assigned to the rooms into real plenum rooms on their own plenum story, which you can see on the canvas of the model editor. From there, you can use the Merge Rooms command to merge the plenums together that are a part of the same zone. Then the plenum for each zone will be exported as a single room in the IESVE GEM.

Granted, we realize that this workflow is pretty manual and uni-directional. There isn’t an easy way to go back to a model with plenums assigned as attributes from the detailed plenum model. But, hopefully, this workflow can tide you over until we figure out how to expose (probably a few) options for how to merge zone geometry in the model editor. I think there are some people who want the option to merge all of the room geometry of the zone together into a single closed volume but I think we can support both this option and a “Merge Plenums Only” option.

2 Likes

i started to play around with this idea today with the following file:
2025-01-7–22-24-58_ECJF-HOK-ME-IESVE Heating_Cooling Loads.pomf (90.5 KB)

using this to assign the plenum depth

from model_editor import Editor
editor = Editor()
model = editor.model

for room in model.room_2ds:
    h = room.user_data.get('Space Ceiling Height', 0)
    if h == 0:
        continue
    # divide the value by 12 assuming the input values are in inches
    room.ceiling_plenum_depth = room.floor_to_ceiling_height - h

editor.update()

and then this to see how they where “grouped”

from model_editor import Editor

editor = Editor()
model = editor.model

# Step 1: Find all "Plenum" rooms and group them by Zone
zone_groups = {}

for room in model.room_2ds:
    if "Plenum" in room.display_name:
        zone = room.user_data.get("Zone")
        if zone:
            zone_groups.setdefault(zone, []).append(room)

# Step 2: Print results
print("🔎 Plenum rooms grouped by Zone:\n")
for zone, rooms in zone_groups.items():
    print(f"🟦 Zone: {zone}")
    for r in rooms:
        print(f"   - {r.display_name}")
    print("")

print(f"âś… Found {sum(len(v) for v in zone_groups.values())} plenum rooms across {len(zone_groups)} zone(s).")

it was successful in telling me which rooms (plenums) shared the same zone data. i wonder if from here it could be taken to an “auto-merge” script?

Hey @rafaelhok ,

Nice! Yea, you are almost there. You’ll just want to use this method to merge together the plenum Room2D zone_groups in your script.

Then, you’ll have an automated way to do this yourself until we eventually add an official workflow in the Model Editor interface.

i may not be using this right but wanted to confirm

from model_editor import Editor

editor = Editor()
model = editor.model

# Step 1: Group plenum rooms by 'Zone'. same as the previous grouping script. 
zone_groups = {}
for room in model.room_2ds:
    if "Plenum" in room.display_name:
        zone = room.user_data.get("Zone")
        if zone:
            zone_groups.setdefault(zone, []).append(room)
# Step 1: Group plenum rooms by 'Zone'. same as the previous grouping script. 

# Step 2: Join room_2ds per zone group====================================== 
joined_rooms = []
for zone, rooms in zone_groups.items():
    if len(rooms) < 2:
        continue  # Nothing to merge

    #static join_room_2ds(room_2ds, min_separation=0, tolerance=0.01, identifier=None)[source]
    merged_room = join_room_2ds(
        room_2ds=rooms,
        min_separation=0,
        tolerance=0.01,
        identifier=None
    )

print(f"Merge applied")
editor.update()

oh wait…would this still need to me to select the plenum rooms? right now I have been using this under the assumption i don’t have to select anything.

No, your script is written to run on the whole model. Not the selected rooms.

Your script is just creating the merged_room but is not replacing the rooms in the model with the new merged room. Now that I think about it, you should probably instead use this method to join the rooms of the same story together. That will edit the story object in place so that you do not need to go deleting and adding rooms from the model after they are joined.

Let me know if it’s clear or if you want me to just edit your script for you.

1 Like

Let me take a stab at it first. it’s a good way for me to learn some more python. if i don’t get it, i will let you know.

1 Like

ok…I would like for your help in fixing the script.

something i do notice when merging plenums even using the standard merge tool, is that the if there are plenums with different depths, it will merge as one room with a single depth/offset vs one room with the depth offset

Before merge:

After merge: Join Faces Off or On has similar results.

Hey @rafaelhok ,

Sorry that this fell through the cracks just an hour after I said that I could help. Here is a version of your script, which should update the model in-place:

from model_editor import Editor

editor = Editor()
model = editor.model

for story in model.stories:
    # Step 1: Group plenums by 'Zone' (same as previous grouping script)
    zone_groups = {}
    for room in story.room_2ds:
        if "Plenum" in room.display_name:
            zone = room.user_data.get("Zone")
            if zone:
                zone_groups.setdefault(zone, []).append(room.identifier)

    # Step 2: Join room_2ds per zone group
    for zone, room_ids in zone_groups.items():
        if len(room_ids) < 2:
            continue  # Nothing to merge
        story.join_room_2ds(room_ids, model.tolerance)

print(f"Merge applied")
editor.update()

That should do the trick.

Yea, there’s not really a good way to represent this with explicit plenum Room2Ds on the Model Editor canvas. Each dragonfly Room2D can have only one planar floor_geometry so a change in floor height within the same Room2D cannot be represented.

We will keep this case in mind when we eventually add options for merging zones when going from Dragonfly (2D schema) to Honeybee (3D schema). As a fully-detailed 3D schema, Honeybee would have no problem representing changes in floor heights like that. So you’ll be able to represent this case in the future.

For the time being, do you think you can work around it by just using consistent plenum depths for all of the plenums in each zone? Or do you want some code that you can put in your script to ensure that all of the plenums have the same floor height before you merge them?

1 Like

Some code that you can put in the script to ensure that all of the plenums have the same floor height before merging might be useful. This could at ideally take care of 50%+ in the model editor vs IESVE (which takes so long)

Hey @rafaelhok ,

Sorry again for the late response. I realized that it is far easier to just make sure that all of the rooms of the same zone have the same ceiling_plenum_depth before you separate them. This could be done with the following script before you separate plenums:

from model_editor import Editor

editor = Editor()
model = editor.model

for story in model.stories:
    # Step 1: Group plenums by 'Zone' (same as previous grouping script)
    zone_groups = {}
    for room in story.room_2ds:
        zone = room.user_data.get("Zone")
        if zone:
            zone_groups.setdefault(zone, []).append(room)

    # Step 2: Ensure all rooms have the same ceiling plenum depth
    for zone, rooms in zone_groups.items():
        if len(room_ids) < 2:
            continue  # Nothing to check
        c_depths = [r.ceiling_plenum_depth for r in rooms]
        avg_plenum = sum(c_depths) / len(c_depths)
        for room in rooms:
            room.ceiling_plenum_depth = avg_plenum 

print('Average ceiling plenum depths applied')
editor.update()

Granted, it is also possible to edit everything after the plenums have been separated but it will be a lot more code. Do you think the shorter script above could serve your purposes well for now?

1 Like

Let me give this a try. The fact that we can create the plenums is already a big plus.

1 Like

Hi @rafaelhok,

We just released a new version of the Revit plugin (v2.293.2) which includes an option for merging plenums or rooms on a zone or a story.

We will publish the official release notes in the next few days with video recordings but I wanted to let you in advance in case you would like to give it a try sooner.

Let us know if you have any questions or comments.

1 Like

I would not mind trying this out. I think the biggest roadblock previously was trying to do merging in Dragonfly (2D schema) with plenums that have different depths which is a common design practice.

Simple example,
2 rooms are in the same zone and have the same floor to floor height, 15 ft, but room 1 has ceilings at 10 ft and room 2 has ceilings at 12 feet.

It could be a case that the rooms be merged and that the plenums be merged and if it handles these cases now, GREAT!

Hi @rafaelhok, thanks!

That’s how it works, and the rooms are merged with different ceiling heights.

That said, I noticed that the plenums are not merged into a single space. I’m not sure if that’s the expected behavior. I’ll document this for @chriswmackey.

1 Like

Hey @mostapha ,

Looking at your screenshot there, it seems that you might have forgotten to solve adjacencies before you exported to GEM with the merge option. As the description of the “Merge Method” says, the routine relies on solved adjacencies to determine which groups of rooms can be merged into contiguous closed volumes.

I know that people usually won’t forget to solve the adjacencies when exporting to DesignBuilder, TRACE, OpenStudio or eQuest but I know adjacency solving has always been optional for IESVE GEM workflows. If we think this new requirement for adjacency solving might trip up new users of the Merge Method with GEM, I can add some extra steps in the dragonfly-iesve GEM exporter, which always erases any existing adjacencies and then re-solves them whenever a Merge Method other than “Do not merge” is selected. Just let me know.

And, yes, @rafaelhok . When used with solved adjacencies, the currently-released merge method will be able to join the plenums with different depths. Let us know if you have any other feedback if you get the chance to test it.