Solid error in python

According to my code, some elements return the correct amount of solids and in other cases it returns a different amount of solids, what I am looking for is for it to return the exact amount of solids.
Because some elements that I have in my model intersect with other elements and their geometry is divided, this means that the element should return these pieces of solids, thus having for each element either one solid or several depending on the case of their intersection, but my code does not return what I need. Could anyone please recommend me what to correct or what is wrong in my code… I leave you the code, THANK YOU VERY MUCH.

import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

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

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

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

def get_line_of_framing(framing, opt):
    if isinstance(framing.Location, DB.LocationCurve):
        return framing.Location.Curve.ToProtoType()
    
    geo_set = framing.get_Geometry(opt)
    for geo in geo_set:
        if isinstance(geo, DB.Line):
            return geo.ToProtoType()
    return None

opt = Options()
opt.DetailLevel = ViewDetailLevel.Coarse
opt.IncludeNonVisibleObjects = True

# Obtener todas las vigas en la categoría de "OST_StructuralFraming"
Vigas = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_StructuralFraming).WhereElementIsNotElementType().ToElements()

# Primer filtrado: solo las vigas que tienen un eje válido
VigasConEjes = []
for viga in Vigas:
    eje = get_line_of_framing(viga, opt)
    if eje is not None:
        VigasConEjes.append(viga)       

# Segundo filtrado: de las vigas con eje, solo aquellas que pueden albergar refuerzos (Rebars)
VigasRebar = []
for e in VigasConEjes:
    # Verificar si la viga es un host válido para barras de refuerzo
    if DBS.RebarHostData.IsValidHost(e):
        VigasRebar.append(e)   
        
# Obtener sólidos de cada viga
SolidosPorViga = []
for e in VigasRebar:
    geoSet = e.get_Geometry(opt)
    solids = []
    if geoSet:
        for g in geoSet:
            if isinstance(g, DB.GeometryInstance):
                # Extraer sólidos de una instancia de geometría
                for gi in g.GetInstanceGeometry():
                    if isinstance(gi, DB.Solid) and gi.Volume > 0:
                        solids.append(gi.ToProtoType())
            elif isinstance(g, DB.Solid) and g.Volume > 0:
                # Agregar sólidos directamente
                solids.append(g.ToProtoType())    
    if solids:
        SolidosPorViga.append(solids)  # Agregar la lista de sólidos a la lista general        
 
    
OUT = SolidosPorViga

A single solid can be built from a union of many solids - use the Solid.Separate() Dynamo function - this will recursively separate a solid down to all element solids
Try

SolidosPorViga.append(Solid.Separate(solids))

Thank you for your response but it returns an error mentioning that it is not a valid attribute.

Hi @AM3D.BIM.STUDIO try ootb solid,separate…could probaly work…

Hi @AM3D.BIM.STUDIO

Try like the below at line 8:

Objeto = Solid.Separate(Geom[0])

1 Like

Thank you, it has worked for me, and I see that it is due to the level of access to the elements within the list, but now what would be your recommendation based on my general code? I left lines above… I mention it because it’s already confusing me…

Try like the below at line 8:

Objeto = [Solid.Separate(s) for s in Geom]

2 Likes

You need iterate through sublist. This has been discussed earlier in your post:

Apparently the problem that arises is accessing the method that is in the library “clr.AddReference(‘ProtoGeometry’)
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS” I think something is blocking it and does not allow the “Separate” method.


For info Solid.Separate() is not a static method

THX c.poupin
Yes you are right it works but my intention is to have the method in my general code because I try to reduce extra nodes, so I want to keep everything necessary possible within a single Python script nodes

import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

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

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

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

def get_line_of_framing(framing, opt):
    if isinstance(framing.Location, DB.LocationCurve):
        return framing.Location.Curve.ToProtoType()
    
    geo_set = framing.get_Geometry(opt)
    for geo in geo_set:
        if isinstance(geo, DB.Line):
            return geo.ToProtoType()
    return None

opt = Options()
opt.DetailLevel = ViewDetailLevel.Coarse
opt.IncludeNonVisibleObjects = True

# Obtener todas las vigas en la categoría de "OST_StructuralFraming"
Vigas = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_StructuralFraming).WhereElementIsNotElementType().ToElements()

# Primer filtrado: solo las vigas que tienen un eje válido
VigasConEjes = []
for viga in Vigas:
    eje = get_line_of_framing(viga, opt)
    if eje is not None:
        VigasConEjes.append(viga)       

# Segundo filtrado: de las vigas con eje, solo aquellas que pueden albergar refuerzos (Rebars)
VigasRebar = []
for e in VigasConEjes:
    # Verificar si la viga es un host válido para barras de refuerzo
    if DBS.RebarHostData.IsValidHost(e):
        VigasRebar.append(e)   
        
# Obtener sólidos de cada viga
SolidosPorViga = []
for e in VigasRebar:
    geoSet = e.get_Geometry(opt)
    solids = []
    if geoSet:
        for g in geoSet:
            if isinstance(g, DB.GeometryInstance):
                # Extraer sólidos de una instancia de geometría
                for gi in g.GetInstanceGeometry():
                    if isinstance(gi, DB.Solid) and gi.Volume > 0:
                        solids.append(gi.ToProtoType())
            elif isinstance(g, DB.Solid) and g.Volume > 0:
                # Agregar sólidos directamente
                solids.append(g.ToProtoType())    
    if solids:
        SolidosPorViga.append(solids)  # Agregar la lista de sólidos a la lista general        
 
    
OUT = SolidosPorViga

It works in Dynamo Sandbox 2.19 and is what ‘node to code’ returns

Strange, I thought PythonNet didn’t support unbound class instance methods like IronPython

I’d do some more tests to be sure

3 Likes

In the code you provided, it implemented the recommendations but it doesn’t work within my Python node.
If you remove the lines of code that give errors you will be able to see what the variable “SolidosPorViga” is returning.
Well I share the dynamo file, .dyn and the revit file where you can try it, .rvt

#LINE CODE ERROR..............
Resultado = []

for e in SolidosPorViga:
    if isinstance(e,list):
        for s in e:
            ss = Solid.Separate(s)
            Resultado.append(ss)
    else:
        sr = Solid.Separate(e)    
        Resultado.append(sr)

ERRROR_VIGAS.dyn (7.0 KB)
error.rvt (6.3 MB)

You are working with Revit (Autodesk.Revit.DB) Solids as they come from the filtered element collector before converting to Dynamo. (note to self :person_facepalming:)

Try converting DB.Solid types with SolidUtils.SplitVolumes(g) before converting to Dynamo

Otherwise you could use solids.append(g.ToProtoType().Separate()) however this is going to introduce lots of lists

Perhaps a helper function

def to_prototype(element):
    geom = None
    if isinstance(element, Solid) and element.Volume > 0:
        geom = element.ToProtoType().Separate()
        
    if isinstance(element, GeometryInstance):
        geom = element.GetInstanceGeometry()
        geom = list(filter(None, [to_prototype(g) for g in geom]))
        
    if geom:
        if len(geom) > 1:
            return geom
        return geom[0] 

Then process something like this

beams = (
    FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_StructuralFraming)
    .WhereElementIsNotElementType()
    .ToElements()
)

opt = Options()
opt.DetailLevel = ViewDetailLevel.Coarse
opt.IncludeNonVisibleObjects = True

output = []
for beam in beams:
    geom = beam.get_Geometry(opt)
    for g in geom:
        solids = to_prototype(g)
        if solids:
            output.append(solids)

OUT = output
1 Like