Obtain the solid of the python element

Good afternoon.
I’m developing a Python code that allows me to obtain the solid form of an element, but with a cascading flow. If the solid form cannot be obtained through one process, it moves to the next, with a final fallback being the export and import of a SAT solid form.

The code works, but I think it’s not very efficient yet. I would appreciate any help in improving it to make it as fast as possible, as the processing times seem very slow.
Code here..!!!

why the external upload though? just post it in the thread. downloading random files will make trust issues kick in. :joy:

I understand what you’re saying.

But the code has detailed documentation to simply describe the code flow, as well as the functions and the purpose of each function, along with some notes on the importance of that code… this has made the code quite long and exceed the allowed length for pasting. I did it this way because I wanted to clearly show the entire code structure, but if the code documentation isn’t necessary, then I’ll try to leave it here.

import clr

clr.AddReference("RevitAPI")

clr.AddReference("RevitAPIUI")

clr.AddReference("RevitServices")

clr.AddReference("ProtoGeometry")

clr.AddReference("RevitNodes")




import time

import random

import System

from System.Collections.Generic import List

import System.IO




import Autodesk

import Autodesk.Revit.DB

import Autodesk.Revit.DB.Architecture

import Autodesk.DesignScript.Geometry




from Autodesk.Revit.DB import *

from Autodesk.DesignScript.Geometry import *




from RevitServices.Persistence import DocumentManager

from RevitServices.Transactions import TransactionManager




import Revit

clr.ImportExtensions(Revit.GeometryConversion)




doc = DocumentManager.Instance.CurrentDBDocument




factor_metros_a_pies = 3.28084




_PIES3_A_METROS3 = 0.0283168




_ID_LINKS = int(BuiltInCategory.OST_RvtLinks)




umbral_volumen_m3 = 0.000625




def ConstruirMapaVinculosGlobal():

    mapa_local = {}

    try:

        colector_links = FilteredElementCollector(doc).OfClass(RevitLinkInstance).ToElements()

        for instancia_link in colector_links:

            try:

                doc_vinculado = instancia_link.GetLinkDocument()

                if doc_vinculado is not None:

                    mapa_local[doc_vinculado.Title] = instancia_link

            except:

                continue

    except:

        pass

    return mapa_local




mapa_vinculos_global = ConstruirMapaVinculosGlobal()




def ObtenerTipoVista3DGlobal():

    try:

        colector_tipos = FilteredElementCollector(doc).OfClass(ViewFamilyType)

        for tipo in colector_tipos:

            if tipo.ViewFamily == ViewFamily.ThreeDimensional:

                return tipo

    except:

        pass

    return None




tipo_vista_3d_global = ObtenerTipoVista3DGlobal()




opciones_geometria_dac = Autodesk.Revit.DB.Options()

opciones_geometria_dac.ComputeReferences = False

opciones_geometria_dac.IncludeNonVisibleObjects = False

opciones_geometria_dac.DetailLevel = Autodesk.Revit.DB.ViewDetailLevel.Fine




def DesenvolverAlElementoNativo(elemento_entrada):

    if elemento_entrada is None:

        return None

    if isinstance(elemento_entrada, Autodesk.Revit.DB.Element):

        return elemento_entrada

    try:

        return elemento_entrada.InternalElement

    except:

        return None




def ObtenerInstanciaVinculo(elemento_nativo):

    if elemento_nativo is None:

        return None

    try:

        if elemento_nativo.Document == doc:

            return None

    except:

        return None

    try:

        titulo_doc = elemento_nativo.Document.Title

        return mapa_vinculos_global.get(titulo_doc, None)

    except:

        pass

    return None




def EsElementoEscalera(elemento_nativo):

    if elemento_nativo is None:

        return False

    try:

        categoria = elemento_nativo.Category

        if categoria is None:

            return False

        return categoria.Id.IntegerValue == int(BuiltInCategory.OST_Stairs)

    except:

        return False




def ExpandirElementoAComponentes(elemento_nativo):

    lst_componentes = []

    if elemento_nativo is None:

        return lst_componentes

    if isinstance(elemento_nativo, Autodesk.Revit.DB.Architecture.Stairs):

        try:

            doc_elemento = elemento_nativo.Document

            for id_run in elemento_nativo.GetStairsRuns():

                ele = doc_elemento.GetElement(id_run)

                if ele is not None:

                    lst_componentes.append(ele)

            for id_landing in elemento_nativo.GetStairsLandings():

                ele = doc_elemento.GetElement(id_landing)

                if ele is not None:

                    lst_componentes.append(ele)

            for id_support in elemento_nativo.GetStairsSupports():

                ele = doc_elemento.GetElement(id_support)

                if ele is not None:

                    lst_componentes.append(ele)

        except:

            pass

        if len(lst_componentes) == 0:

            lst_componentes.append(elemento_nativo)

    else:

        lst_componentes.append(elemento_nativo)

    return lst_componentes




def RecolectarSolidosDeContenedorGeometria(contenedor_geometria, lst_destino):

    if contenedor_geometria is None:

        return

    try:

        for objeto in contenedor_geometria:

            if objeto is None:

                continue

            if isinstance(objeto, Autodesk.Revit.DB.Solid):

                try:

                    if objeto.Volume > 0:

                        lst_destino.append(objeto)

                except:

                    continue

            elif isinstance(objeto, GeometryInstance):

                try:

                    RecolectarSolidosDeContenedorGeometria(objeto.GetInstanceGeometry(), lst_destino)

                except:

                    continue

    except:

        pass




def ExtraerSolidosRevitDelElemento(elemento_nativo):

    lst_solidos = []

    if elemento_nativo is None:

        return lst_solidos

    for componente in ExpandirElementoAComponentes(elemento_nativo):

        try:

            geometria = componente.get_Geometry(opciones_geometria_dac)

            if geometria is not None:

                RecolectarSolidosDeContenedorGeometria(geometria, lst_solidos)

        except:

            continue

    return lst_solidos




def FiltrarSolidosPorUmbralVolumen(lst_solidos_revit):

    lst_filtrados = []

    if lst_solidos_revit is None:

        return lst_filtrados

    for solido_iter in lst_solidos_revit:

        if solido_iter is None:

            continue

        try:

            volumen_m3 = solido_iter.Volume * _PIES3_A_METROS3

            if volumen_m3 >= umbral_volumen_m3:

                lst_filtrados.append(solido_iter)

        except:

            continue

    return lst_filtrados




def UnirSolidosRevitParcialmente(lst_solidos_revit):

    if lst_solidos_revit is None or len(lst_solidos_revit) == 0:

        return []

    if len(lst_solidos_revit) == 1:

        return [lst_solidos_revit[0]]

    lst_resultado = [lst_solidos_revit[0]]

    for indice in range(1, len(lst_solidos_revit)):

        solido_a_unir = lst_solidos_revit[indice]

        if solido_a_unir is None:

            continue

        unido_correctamente = False

        for indice_acumulado in range(len(lst_resultado)):

            try:

                resultado = BooleanOperationsUtils.ExecuteBooleanOperation(

                    lst_resultado[indice_acumulado], solido_a_unir, BooleanOperationsType.Union)

                if resultado is not None and resultado.Volume > 0:

                    lst_resultado[indice_acumulado] = resultado

                    unido_correctamente = True

                    break

            except:

                continue

        if not unido_correctamente:

            lst_resultado.append(solido_a_unir)

    return lst_resultado




def ConvertirSolidoRevitAProtoType(solido_revit):

    if solido_revit is None:

        return None

    try:

        if hasattr(solido_revit, 'IsValidObject') and not solido_revit.IsValidObject:

            return None

        solido_dynamo = solido_revit.ToProtoType()

        if solido_dynamo is None:

            return None

        return solido_dynamo

    except:

        return None




def ConvertirListaSolidosRevitAProtoType(lst_solidos_revit):

    lst_resultado = []

    todos_convirtieron = True

    for solido_revit_iter in lst_solidos_revit:

        solido_dynamo = ConvertirSolidoRevitAProtoType(solido_revit_iter)

        if solido_dynamo is not None:

            lst_resultado.append(solido_dynamo)

        else:

            todos_convirtieron = False

    return lst_resultado, todos_convirtieron




def UnirSolidosDynamoParcialmente(lst_solidos_dynamo):

    if lst_solidos_dynamo is None or len(lst_solidos_dynamo) == 0:

        return []

    if len(lst_solidos_dynamo) == 1:

        return [lst_solidos_dynamo[0]]

    lst_resultado = [lst_solidos_dynamo[0]]

    for indice in range(1, len(lst_solidos_dynamo)):

        solido_a_unir = lst_solidos_dynamo[indice]

        if solido_a_unir is None:

            continue

        unido_correctamente = False

        for indice_acumulado in range(len(lst_resultado)):

            try:

                resultado = Autodesk.DesignScript.Geometry.Solid.ByUnion(

                    [lst_resultado[indice_acumulado], solido_a_unir])

                if resultado is not None and resultado.Volume > 0:

                    lst_resultado[indice_acumulado] = resultado

                    unido_correctamente = True

                    break

            except:

                continue

        if not unido_correctamente:

            lst_resultado.append(solido_a_unir)

    return lst_resultado




def ExtraerSolidosDynamoDirecto(elemento_entrada, elemento_nativo):

    lst_solidos = []

    volumen_total_m3 = 0.0

    elemento_dynamo = None

    if elemento_entrada is not None and not isinstance(elemento_entrada, Autodesk.Revit.DB.Element):

        elemento_dynamo = elemento_entrada

    else:

        try:

            elemento_dynamo = elemento_nativo.ToDSType(True)

        except:

            elemento_dynamo = None

    if elemento_dynamo is None:

        return lst_solidos, volumen_total_m3

    try:

        geometria_dynamo = elemento_dynamo.Geometry()

    except:

        return lst_solidos, volumen_total_m3

    if geometria_dynamo is None:

        return lst_solidos, volumen_total_m3

    for objeto_geo in geometria_dynamo:

        try:

            if isinstance(objeto_geo, Autodesk.DesignScript.Geometry.Solid):

                volumen_solido = objeto_geo.Volume

                if volumen_solido >= umbral_volumen_m3:

                    lst_solidos.append(objeto_geo)

                    volumen_total_m3 = volumen_total_m3 + volumen_solido

        except:

            continue

    return lst_solidos, volumen_total_m3




def ObtenerBoundingBoxConTolerancia(elemento_nativo, tolerancia_inferior_metros, tolerancia_superior_metros):

    if elemento_nativo is None:

        return None

    try:

        bbox = elemento_nativo.get_BoundingBox(None)

        if bbox is None:

            return None

        punto_min = bbox.Min

        punto_max = bbox.Max

        instancia_vinculo = ObtenerInstanciaVinculo(elemento_nativo)

        if instancia_vinculo is not None:

            try:

                transform = instancia_vinculo.GetTransform()

                punto_min = transform.OfPoint(punto_min)

                punto_max = transform.OfPoint(punto_max)

            except:

                pass

        tol_inf_pies = tolerancia_inferior_metros * factor_metros_a_pies

        tol_sup_pies = tolerancia_superior_metros * factor_metros_a_pies

        punto_minimo_exp = XYZ(

            punto_min.X - tol_inf_pies,

            punto_min.Y - tol_inf_pies,

            punto_min.Z - tol_inf_pies)

        punto_maximo_exp = XYZ(

            punto_max.X + tol_sup_pies,

            punto_max.Y + tol_sup_pies,

            punto_max.Z + tol_sup_pies)

        nuevo_bbox = BoundingBoxXYZ()

        nuevo_bbox.Min = punto_minimo_exp

        nuevo_bbox.Max = punto_maximo_exp

        return nuevo_bbox

    except:

        return None




def CrearNuevaVista3D():

    try:

        tipo_3d = tipo_vista_3d_global

        if tipo_3d is None:

            return None

        nombre_base = "AM3D_vista3d_" + str(random.randint(1000, 9999))

        TransactionManager.Instance.EnsureInTransaction(doc)

        vista = View3D.CreateIsometric(doc, tipo_3d.Id)

        nombre_final = nombre_base

        contador = 1

        while True:

            try:

                vista.Name = nombre_final

                break

            except:

                nombre_final = nombre_base + "_" + str(contador)

                contador = contador + 1

                if contador > 100:

                    break

        TransactionManager.Instance.TransactionTaskDone()

        return vista

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def CrearSectionBox(vista_3d, bbox_xyz):

    if vista_3d is None or bbox_xyz is None:

        return None

    if not isinstance(vista_3d, View3D):

        return None

    TransactionManager.Instance.EnsureInTransaction(doc)

    try:

        if not vista_3d.IsSectionBoxActive:

            vista_3d.IsSectionBoxActive = True

        vista_3d.SetSectionBox(bbox_xyz)

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        TransactionManager.Instance.ForceCloseTransaction()

        return None




def AislarElementoEnVista(elemento_nativo, vista_3d, resetear=True):

    if vista_3d is None or elemento_nativo is None:

        return None

    doc_vista = vista_3d.Document

    try:

        if not vista_3d.CanUseTemporaryVisibilityModes():

            return None

        if elemento_nativo.Document != doc_vista:

            return None

        lista_ids = List[ElementId]()

        lista_ids.Add(elemento_nativo.Id)

        TransactionManager.Instance.EnsureInTransaction(doc_vista)

        if resetear:

            try:

                vista_3d.DisableTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate)

            except:

                pass

        vista_3d.IsolateElementsTemporary(lista_ids)

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def ModificarVisualizacionVinculo(instancia_vinculo, vista_3d):

    if instancia_vinculo is None or vista_3d is None:

        return None

    if not isinstance(instancia_vinculo, RevitLinkInstance):

        return None

    doc_vista = vista_3d.Document

    try:

        if vista_3d.ViewTemplateId != ElementId.InvalidElementId:

            return vista_3d

        TransactionManager.Instance.EnsureInTransaction(doc_vista)

        try:

            overrides = vista_3d.GetLinkOverrides(instancia_vinculo.Id)

            overrides.SetLinkVisibilityType(LinkVisibility.ByHostView)

            vista_3d.SetLinkOverrides(instancia_vinculo.Id, overrides)

        except:

            pass

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def AislarCategoriaEnVista(elemento_nativo, vista_3d):

    if elemento_nativo is None or vista_3d is None:

        return None

    doc_vista = vista_3d.Document

    try:

        categoria = elemento_nativo.Category

        if categoria is None:

            return None

        id_categoria = categoria.Id

        es_vinculo = elemento_nativo.Document != doc_vista

        TransactionManager.Instance.EnsureInTransaction(doc_vista)

        for cat in doc_vista.Settings.Categories:

            try:

                id_cat = cat.Id

                if es_vinculo:

                    debe_ocultar = (id_cat != id_categoria and id_cat.IntegerValue != _ID_LINKS)

                    vista_3d.SetCategoryHidden(id_cat, debe_ocultar)

                else:

                    vista_3d.SetCategoryHidden(id_cat, id_cat != id_categoria)

            except:

                pass

        TransactionManager.Instance.TransactionTaskDone()

        return vista_3d

    except:

        try:

            TransactionManager.Instance.ForceCloseTransaction()

        except:

            pass

        return None




def LeerVolumenComputadoRevit(elemento_nativo):

    if elemento_nativo is None:

        return 0

    try:

        param = elemento_nativo.get_Parameter(BuiltInParameter.HOST_VOLUME_COMPUTED)

        if param is None:

            return 0

        volumen_pies3 = param.AsDouble()

        if volumen_pies3 <= 0:

            return 0

        return round(volumen_pies3 * _PIES3_A_METROS3, 5)

    except:

        return 0




def CalcularVolumenEscaleraPorSubcomponentes(elemento_nativo):

    volumen_pies3 = 0.0

    try:

        lst_solidos = ExtraerSolidosRevitDelElemento(elemento_nativo)

        for solido in lst_solidos:

            try:

                volumen_pies3 = volumen_pies3 + solido.Volume

            except:

                continue

    except:

        pass

    return volumen_pies3 * _PIES3_A_METROS3




def ExtraerSolidosViaSAT(elemento_nativo, es_escalera, id_texto_elemento):

    if elemento_nativo is None:

        return []

    bbx = ObtenerBoundingBoxConTolerancia(elemento_nativo, 0, 0)

    if bbx is None:

        return []

    es_vinculo_local = False

    try:

        es_vinculo_local = elemento_nativo.Document != doc

    except:

        es_vinculo_local = False

    volumen_elemento_m3 = 0

    if not es_escalera:

        volumen_elemento_m3 = LeerVolumenComputadoRevit(elemento_nativo)

        if volumen_elemento_m3 == 0:

            return []

    else:

        volumen_escalera_m3 = CalcularVolumenEscaleraPorSubcomponentes(elemento_nativo)

        if volumen_escalera_m3 == 0:

            return []

    vista = None

    vista_copia = None

    geometria_lista = []

    try:

        if es_vinculo_local:

            instancia_vinculo = ObtenerInstanciaVinculo(elemento_nativo)

            if instancia_vinculo is None:

                return []

            vista = CrearNuevaVista3D()

            if vista is None:

                return []

            vista = CrearSectionBox(vista, bbx)

            vista = AislarElementoEnVista(instancia_vinculo, vista)

            vista = ModificarVisualizacionVinculo(instancia_vinculo, vista)

            vista = AislarCategoriaEnVista(elemento_nativo, vista)

        else:

            vista = CrearNuevaVista3D()

            if vista is None:

                return []

            vista = CrearSectionBox(vista, bbx)

            vista = AislarElementoEnVista(elemento_nativo, vista)

        if vista is None:

            return []

        TransactionManager.Instance.EnsureInTransaction(doc)

        vista_copia_id = vista.Duplicate(ViewDuplicateOption.Duplicate)

        doc.Regenerate()

        vista_copia = doc.GetElement(vista_copia_id)

        if vista_copia is None:

            TransactionManager.Instance.TransactionTaskDone()

            return []

        try:

            vista_copia.Name = "AM3D_SAT_" + id_texto_elemento + "_" + str(random.randint(1000, 9999))

        except:

            pass

        TransactionManager.Instance.TransactionTaskDone()

        TransactionManager.Instance.EnsureInTransaction(doc)

        try:

            vista_copia.DisableTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate)

        except:

            pass

        doc.Regenerate()

        TransactionManager.Instance.TransactionTaskDone()

        ruta = System.IO.Path.GetTempPath()

        timestamp_unico = str(int(time.perf_counter() * 1000000)) + "_" + str(random.randint(1000, 9999))

        nombre = "temp_sat_" + id_texto_elemento + "_" + timestamp_unico + ".sat"

        ruta_completa = System.IO.Path.Combine(ruta, nombre)

        doc.Export(ruta, nombre, List[ElementId]([vista_copia.Id]), SATExportOptions())

        geometria_raw = Autodesk.DesignScript.Geometry.Geometry.ImportFromSAT(ruta_completa)

        for g in geometria_raw:

            geometria_lista.append(g)

    except:

        geometria_lista = []

    finally:

        TransactionManager.Instance.EnsureInTransaction(doc)

        try:

            if vista_copia is not None:

                doc.Delete(vista_copia.Id)

            if vista is not None:

                doc.Delete(vista.Id)

        except:

            pass

        TransactionManager.Instance.TransactionTaskDone()

    solidos = []

    for g in geometria_lista:

        try:

            if isinstance(g, Autodesk.DesignScript.Geometry.Solid):

                if g.Volume > 0:

                    solidos.append(g)

        except:

            pass

    if len(solidos) == 0:

        return []

    if es_escalera:

        if len(solidos) == 1:

            return [solidos[0]]

        try:

            resultado_union = Autodesk.DesignScript.Geometry.Solid.ByUnion(solidos)

        except:

            return []

        if isinstance(resultado_union, Autodesk.DesignScript.Geometry.Solid):

            return [resultado_union]

        try:

            solido_mayor = None

            volumen_mayor = -1

            for s in resultado_union:

                try:

                    v = s.Volume

                except:

                    v = 0

                if v > volumen_mayor:

                    volumen_mayor = v

                    solido_mayor = s

            if solido_mayor is not None:

                return [solido_mayor]

        except:

            pass

        return []

    solido_mas_cercano = None

    diferencia_minima = float("inf")

    for s in solidos:

        try:

            v = round(s.Volume, 5)

        except:

            v = 0

        dif = abs(volumen_elemento_m3 - v)

        if dif < diferencia_minima:

            diferencia_minima = dif

            solido_mas_cercano = s

    if solido_mas_cercano is None:

        return []

    return [solido_mas_cercano]




def ProcesarEscaleraAPI(elemento_nativo, lst_solidos_revit, id_texto_elemento):

    cantidad = len(lst_solidos_revit)

    if cantidad == 1:

        solido_dynamo = ConvertirSolidoRevitAProtoType(lst_solidos_revit[0])

        if solido_dynamo is not None:

            return [solido_dynamo]

        return None

    lst_unidos_revit = UnirSolidosRevitParcialmente(lst_solidos_revit)

    if len(lst_unidos_revit) == 1:

        solido_dynamo = ConvertirSolidoRevitAProtoType(lst_unidos_revit[0])

        if solido_dynamo is not None:

            return [solido_dynamo]

        return None

    lst_dynamo, todos_ok = ConvertirListaSolidosRevitAProtoType(lst_unidos_revit)

    if todos_ok:

        return UnirSolidosDynamoParcialmente(lst_dynamo)

    return None




def ProcesarNoEscaleraAPI(lst_solidos_revit):

    lst_dynamo, todos_ok = ConvertirListaSolidosRevitAProtoType(lst_solidos_revit)

    if todos_ok:

        return lst_dynamo

    return None




def ObtenerSolidosDelElemento(elemento_entrada):

    elemento_nativo = DesenvolverAlElementoNativo(elemento_entrada)

    if elemento_nativo is None:

        return []

    try:

        id_texto_elemento = str(elemento_nativo.Id.IntegerValue)

    except:

        id_texto_elemento = "Sin_Id"

    es_escalera = EsElementoEscalera(elemento_nativo)




    volumen_referencia_m3 = LeerVolumenComputadoRevit(elemento_nativo)




    lst_solidos_dynamo, volumen_dynamo_m3 = ExtraerSolidosDynamoDirecto(elemento_entrada, elemento_nativo)

    if len(lst_solidos_dynamo) > 0:

        flujo1_aceptable = True

        if volumen_referencia_m3 > 0:

            if volumen_dynamo_m3 < (volumen_referencia_m3 * 0.90):

                flujo1_aceptable = False

        if flujo1_aceptable:

            if es_escalera:

                return UnirSolidosDynamoParcialmente(lst_solidos_dynamo)

            return lst_solidos_dynamo




    lst_solidos_revit = ExtraerSolidosRevitDelElemento(elemento_nativo)

    lst_solidos_revit = FiltrarSolidosPorUmbralVolumen(lst_solidos_revit)

    if len(lst_solidos_revit) == 0:

        return []

    if es_escalera:

        resultado = ProcesarEscaleraAPI(elemento_nativo, lst_solidos_revit, id_texto_elemento)

    else:

        resultado = ProcesarNoEscaleraAPI(lst_solidos_revit)




    if resultado is None:

        if es_escalera:

            return ExtraerSolidosViaSAT(elemento_nativo, True, id_texto_elemento)

        return ExtraerSolidosViaSAT(elemento_nativo, False, id_texto_elemento)

    return resultado




try:

    valor_entrada = IN[0]

except:

    valor_entrada = None




es_lista = False

if valor_entrada is not None:

    if isinstance(valor_entrada, Autodesk.Revit.DB.Element):

        es_lista = False

    elif hasattr(valor_entrada, "InternalElement") and valor_entrada.InternalElement is not None:

        es_lista = False

    else:

        try:

            iter(valor_entrada)

            es_lista = not isinstance(valor_entrada, str)

        except:

            es_lista = False




if es_lista:

    lst_salida = []

    for elemento_iter in valor_entrada:

        lst_salida.append(ObtenerSolidosDelElemento(elemento_iter))

    OUT = lst_salida

else:

    OUT = ObtenerSolidosDelElemento(valor_entrada)

im not entirely sure what you are trying to achieve, but at a glance, i can spot two things:

  • view generation
  • bool ops

these are slow indeed, i would bypass them first if possible and benchmark again.

edit: the ToProtoType calls. i would use native geometries only or all-in protogeometries. I have never tested how punishing these conversions are but if I’m looking for culprits, this comes to my mind first.

Any time you do try: thing, except: continue you are punishing yourself with a major penalty in the code. Instead of trying you should attempt to identify which method should work as the first step.

That said, using the out of the box node Element.Solids is going to be orders of magnitude more effective and make the code maintainable. You need a VERY good reason to try and circumvent that and you haven’t shared the case for such yet. Every time the the underlaying geometry engine Dynamo utilizes is updated (I think that’s happened something like 3x in the last three years) you’re apt to have to start this deep dive all over again (and at that point each try: thing except: continue becomes an anchor tied to your code base’s life jacket as the errors will be suppressed entirely).

Implement a logger and identify which functions take the most time (remember to capture all errors in the logger).

What “BimAmbit” mentions is true: 3D views are created to help focus on an element, and through a process, that view and its SAT solid are exported, from which a solid is recovered. This process is necessary because “element.geometry” fails to extract the solid if that method is attempted. That’s why “BimAmbit” mentioned that the code requires comments and documentation to understand the logic. Because of this, adding the documentation to the code would make it too large to include in the post. Therefore, I uploaded it as a file to a public server so that anyone can download it, read it correctly, understand the logic, and make corrections or recommendations based on that understanding.

The use of ToProtoType is necessary because it requires obtaining a solid that Dynamo can read and work with. Dynamo’s capabilities in geometry manipulation are far greater than Revit’s. In short, Revit falls short of what Dynamo can do. I have researched and verified this extensively, as both kernels work similarly but not identically. Many believe that Dynamo is not very complete or robust compared to the Revit API, but that is not true. On the contrary, it has a greater scope than the Revit API itself, since the Revit API does not expose many aspects of geometric manipulation.

Thanks for the feedback, “jacob.small,” about using Dynamo’s “Element.Geometry.” I’ve measured the times compared to the Revit API, and the difference is only 50ms to 30ms, which is almost negligible, with the Revit API being much faster. I was unaware of the update dates or times for Dynamo’s geometry engine, and I’ve noticed in my numerous tests that it exhibits several strange behaviors. I thought it was due to a corrupted model or element in my system. Now that you mention it, this would have saved me six months of investigations, trial and error, trying to pinpoint the source of various errors. It works in some versions but not in others, and so on. I would appreciate it if you could tell me which website mentions these geometry engine updates so I don’t miss them. Are these updates documented as a record of the improvements made, or is it simply labeled “Geometry Engine Update”?

Tienes mucha razón “c.poupin”, créeme lo he implementado un loggins altamente detallado para saber línea por línea, método, propiedad el tiempo que le toma en realizar cada acción y el resultado es que exportar e importar un solido a dynamo tomando como intermedio a revit es lo mas costoso, pero este proceso desde mi percepción es inevitable y es ahí donde radica mi problema y solicitud de ayuda, pues requiero otras estrategias de obtener ese solido que no sean tan costosas como este enfoque que he planteado ya que se como 20 segundo por cada solido o elemento.

While 50ms doesn’t seem like much, every time I have seen people attempt this the eventual result (could be days, weeks, months or years down the line) is that performance takes a significant hit due to changes or just due to scaling the dataset that’s being used.

The geometry engine is closed source, so you don’t have direct insight. You can assume it’ll be hit 1x a year at a minimum though.

In the end you’re trying to recreate the Element.Solids node without the context which only the Revit team has (well, the Revit team, Civil 3D team, and other tools that ‘host’ Dynamo - no one outside of Autodesk knows directly), and as a result you’re getting stuck where you are. There are two code bases to this - ASM and LibG - which are not always updated in sync. Ways to identify which version you’re in are tough to come by as your computer can have multiple ASM and LibG versions on your system (makes sense as you can run Revit 2024 and 2027 concurrently), and there isn’t a direct “which version of ASM am I using” API call until Dynamo 4.1. As such you have a few things to do:

  1. Check the release notes. Geometry updates, ASM and LibG show up all over the place. I think you’ll have 7
  2. Pull the version of LibG in use for your Dynamo session which can be done with one line of Python after you import Dynamo Dynamo.Events.ExecutionEvents.ActiveSession.GetParameterValue("GeometryFactoryFileName").
  3. For Dynamo 4.1 and beyond you can pull the version of ASM via the API (pr here)

Now if you keep to just inside the Revit API for your work all of this is moot as you’re not converting anything so both ASM and LibG are irrelevant. This would provide the most speed and consistency overall. But as soon as you convert to Dynamo geometry you introduce the complicating factors. Now this doesn’t mean you shouldn’t pursue the effort, but you should go into this knowing this is a LOT of effort to first build and then (more importantly) even more effort maintain what is essentially an existing API call.

yeah that’s reasonable. I see many people eventually gravitate towards dynamo mainly because of this.