GetBoundingElementFace and Membrane Layer

Hello, i’m trying to get the layer of the walls that delimitate a room, inquiring the faces of the wall. All works well excpt for the wall where membrane layer is set. In that case the method get only the finish layer before the membrane. There is a way to get the membrane instead?

Hi Pietro, welcome :wave:

Please take time to read the following post How to get help on the Dynamo forums

Can you provide more information? Are you using python? Can you provide a screen shot of your graph?

My guess is that it is something to do with a wall type core and shell layers, however it is very difficult to diagnose the issue without additional information

2 Likes

Hi,

After several attempts, I think the only solution is to analyze the layers of the internal wall structure with CompoundStructure

code (tested with IronPython2 or 3)


import sys
import clr
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

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

room = UnwrapElement(IN[0])
debug = []
# 
calculator = SpatialElementGeometryCalculator(doc)
results_data_room = []

if calculator.CanCalculateGeometry(room):
    results = calculator.CalculateSpatialElementGeometry(room)
    roomSolid = results.GetGeometry()
    roomMaterial = list()
    for face in roomSolid.Faces:
        bbx_face = face.GetBoundingBox()
        minPt_face = face.Evaluate(bbx_face.Min)
        for subface in results.GetBoundaryFaceInfo(face):
            if subface.SubfaceType == SubfaceType.Side:     
                boundingElement = subface.GetBoundingElementFace()
                lnkelemId = subface.SpatialBoundaryElement 
                materialId = boundingElement.MaterialElementId  
                material = doc.GetElement(materialId)
                print(f"material : {material.Name}")
                elem = doc.GetElement(lnkelemId.HostElementId )
                if isinstance(elem, Wall):
                    comp_structure = elem.WallType.GetCompoundStructure()
                    #  get ordered layers exterior to interior
                    layers = list(comp_structure.GetLayers())
                    # if the 1st or the last item is a membrane
                    if layers[0].Function == MaterialFunctionAssignment.Membrane or layers[-1].Function == MaterialFunctionAssignment.Membrane:
                        #
                        # compute interior line and exterior line
                        line_location = elem.Location.Curve
                        offset_ext = comp_structure.GetOffsetForLocationLine(WallLocationLine.FinishFaceExterior)
                        offset_int = comp_structure.GetOffsetForLocationLine(WallLocationLine.FinishFaceInterior)
                        # reverse offsets if wall is flipped
                        if elem.Flipped :
                            offset_ext *= -1
                            offset_int *= -1
                        line_exterior = line_location.CreateOffset(offset_int, XYZ.BasisZ)
                        line_interior = line_location.CreateOffset(offset_ext, XYZ.BasisZ)
                        #
                        # obtenir la couche la plus proche de la face de la pièce
                        pairs_line_layer = [[line_exterior, layers[0]], [line_interior, layers[-1]]]
                        pairs_line_layer.sort(key = lambda x : x[0].Project(minPt_face).Distance )
                        # get material
                        material = doc.GetElement(pairs_line_layer[0][1].MaterialId)

                surfaceDs = face.ToProtoType()
                results_data_room.append([surfaceDs[0], elem, material.Name])
    results.Dispose()

OUT =  results_data_room
1 Like

Hi, thanks for the reply. My idea was the same, but i did all with nodes with a lot of workaround. This means that it’s very difficult to control it. So i’ll try you code asap.
The goal of my script is to popolate the room’s information about walls in a building with more the 1000 rooms…

Edit: I looked at your code and maybe (I am not a programmer) your code is not distinguishing the right side. Let me explain: a wall separating two rooms might have a membrane of one type on one side and a membrane of another type on the other. Or it might have no membrane at all on one side. That’s why, in the sequence I’ve set up, I check to see if the surface it has identified for that room is on the outside or inside, and then I try to check if there is a membrane on that side (I do a -1 or +1 depending on whether it’s before or after 3).

1 Like

Can you upload a sample RVT file with your wall types? I’ll check it out.

Hi, I cannot upload because i’m new, if you know how to do, please tell me. I can screenshoot an example:

In this case my script work, but is a dirty code and i cannot check it in more complex situation

I’m updating your trust level so you should be able to post files soon, but you can also use a 3rd party service such as Dropbox, Google Drive, Box, OneDrive, Etc…

1 Like

Thank you!

1 Like

Here my script and a simple rvt
tess.rvt (5.6 MB)
Test Room 1.dyn (41.0 KB)

1 Like

I’ve tested my code with your RVT, it works fine, but my solution may fail with other complex walls.

room faces material

Can you send me your script. I would like to mix it up with mine

It was provided here: GetBoundingElementFace and Membrane Layer - #3 by c.poupin

1 Like

Here is an update version for a list of rooms at input

code IronPython2 or 3 (Possible with CPython3 see comments in code)

import sys
import clr
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

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

def get_room_finish_materials(calculator, room):
    roomMaterials = []
    if calculator.CanCalculateGeometry(room): # if you use CPython3 / PythonNet use .Net Reflection
        results = calculator.CalculateSpatialElementGeometry(room) # if you use CPython3 / PythonNet use .Net Reflection
        roomSolid = results.GetGeometry()
        for face in roomSolid.Faces:
            bbx_face = face.GetBoundingBox()
            minPt_face = face.Evaluate(bbx_face.Min)
            for subface in results.GetBoundaryFaceInfo(face):
                if subface.SubfaceType == SubfaceType.Side:     
                    boundingElement = subface.GetBoundingElementFace()
                    lnkelemId = subface.SpatialBoundaryElement 
                    materialId = boundingElement.MaterialElementId  
                    #
                    material = doc.GetElement(materialId)
                    elem = doc.GetElement(lnkelemId.HostElementId )
                    if isinstance(elem, Wall):
                        comp_structure = elem.WallType.GetCompoundStructure()
                        #  get ordered layers exterior to interior
                        layers = list(comp_structure.GetLayers())
                        # if the 1st or the last item is a membrane
                        if layers[0].Function == MaterialFunctionAssignment.Membrane or layers[-1].Function == MaterialFunctionAssignment.Membrane:
                            #
                            # compute interior line and exterior line
                            line_location = elem.Location.Curve
                            offset_ext = comp_structure.GetOffsetForLocationLine(WallLocationLine.FinishFaceExterior)
                            offset_int = comp_structure.GetOffsetForLocationLine(WallLocationLine.FinishFaceInterior)
                            # reverse offsets if wall is flipped
                            if elem.Flipped :
                                offset_ext *= -1
                                offset_int *= -1
                            line_exterior = line_location.CreateOffset(offset_int, XYZ.BasisZ)
                            line_interior = line_location.CreateOffset(offset_ext, XYZ.BasisZ)
                            #
                            # obtain the layer closest to the part face
                            pairs_line_layer = [[line_exterior, layers[0]], [line_interior, layers[-1]]]
                            pairs_line_layer.sort(key = lambda x : x[0].Project(minPt_face).Distance )
                            # get material and override the variable
                            material = doc.GetElement(pairs_line_layer[0][1].MaterialId)
                    roomMaterials.append(material)
        results.Dispose()
    return roomMaterials
    
toList = lambda x : x if hasattr(x, "__iter__") and not isinstance(x, (str, System.String)) else [x]
rooms = toList(UnwrapElement(IN[0]))
# 
calculator = SpatialElementGeometryCalculator(doc)


OUT =  rooms, [get_room_finish_materials(calculator, room) for room in rooms]
1 Like

OK, that sounds just what I need. However, when I copy-paste your code into a python node I get an error and I don’t understand why.
I will paste the screenshot

as I mention in my post use Ironpython engine (2 or 3), or if you are an advanced user of Python and .Net try to use .Net Reflection (it can be a good exercise)

Thank you! I just select the right engine and now works.
Now I have to implement the rest of the code and try it in a complex situation. I would like to test how it works when “null” is found.
If it is useful for the community, I can post the final result when it is finished. The goal now is both to write the material in the room (I would like to make a check) and to export the whole list into excel

1 Like

Thanks to @c.poupin , I created this script that creates three sheets for me in Excel: 1) Wall coverings 2) Doors 3) Windows divided by room.
It remains to figure out how to get the ceiling as I have not found the way yet.
I attach the file here if anyone may need it.
Room Informations in Excels.dyn (87.7 KB)

try to group these nodes, managing the input data to have just one “Data.ExportToExcel” node

I tried but:

  1. I dind’t understand how to create a single list with all the elements that I need
    or
  2. I didn’t understand how to say to write differents worksheet for every list.

So i choose the shorter way: 3 x output in excels!

P.S. do u have any tips about false cieling tupe in the rooms?

Perhaps you need to modify the process (to obtain the membrane layer), by recovering the associated geometric surface for each layer, I haven’t tried this.