Best way to optimize Curve.ExtrudeAsSolid for 3D Polygons?

Well, I lied. The final final solution was actually to go back to corridor creation, since we needed full IFC 4.3 compliance. Achieving that with solids would have been either impossible or would have required third-party software. In the end, I had to rebuild everything using corridor creation — 8 baselines and around 1,800 regions — with targeting and parameter overriding applied at every 5 meters along a 100 km corridor, all fully automated with Dynamo.

Surprisingly, this approach was still faster than the solid-based method. The full process took about two hours to run, mostly due to rebuilding the corridor twice; the rest of the operations were almost instantaneous thanks to Dynamo automation.

That said, the solid-based method was a working concept — the entire model was built that way at one point. However, beyond the lack of IFC 4.3 compliance, it also produced significantly larger file sizes compared to the corridor-based IFC.

This is the code I used for the solid method. Please note that while it works like charm, this is not finished product and I also tried to optimize RAM usage, so you can find Dispose() and additional garbage collection here and there (probably totally unnecessary as the transaction ideally does that, but I had to try):

# Load the Python Standard and DesignScript Libraries
import sys
import clr
import gc

# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *
import Autodesk.Civil as civil

# The inputs to this node will be stored as a list in the IN variables.
dataEnteringNode = IN
adoc = Application.DocumentManager.MdiActiveDocument
editor = adoc.Editor
zones = IN[0]
structure = IN[1]
solidlistout = []
propertiesout = []
effectivewidthlist = []

zonenumbers = IN[2]

with adoc.LockDocument():
    with adoc.Database as db:

        for zone, thickness, zonenumber in zip(zones,structure,zonenumbers):
            
            with db.TransactionManager.StartTransaction() as t:
                
                
                solidsforlist = []
                propertyforlist = []
                plnumber = 1
                
                surfaceId = TinSurface.Create(db, "temp")
                
                for pline,layerthickness in zip(zone,thickness):
                    if pline:
            
                        # Place your code below
                        plineid = pline.InternalObjectId
                        plineobj = t.GetObject(plineid, OpenMode.ForWrite)
                        
                        point_collection = Point3dCollection()
                        # Loop through vertices and add to collection
                        for vertex_id in plineobj:
                            vertex = t.GetObject(vertex_id, OpenMode.ForWrite)
                            point_collection.Add(vertex.Position)
                        
                        
                        # Create TIN Surface
                        #surfaceId = TinSurface.Create(db, "temp")
                        surface = t.GetObject(surfaceId, OpenMode.ForWrite)
                        
                        # Add breakline
                        surface.BreaklinesDefinition.AddStandardBreaklines(point_collection, 1.0, 1.0, 1.0, 0.0)
                        
                        
                        triangles = surface.Triangles
                        
                        # check if surface has triangles
                        if triangles.Count == 0:
                            hastriangles = False
                            editor.WriteMessage(f"\nsurface has no area, invalid boundary")
                        # check if surface size and shape to exclude invalid zone definitions
                        else:
                            area = surface.GetTerrainProperties().SurfaceArea2D
                            perimeter = plineobj.Length
                            effectivewidth = area / (perimeter/2)
                            effectivewidthlist.append(effectivewidth)
                            hastriangles = True
            
                        # Add as boundary
                        if hastriangles and area > 5 and (effectivewidth < 5 and effectivewidth > 2):
                            surface.BoundariesDefinition.AddBoundaries(point_collection, 1.0, civil.SurfaceBoundaryType.Outer, False)
                            
                            layername = "zone_" + zonenumber
                            # Extract as solid
                            solids = surface.CreateSolidsAtDepth(layerthickness, layername, 10)
                            editor.WriteMessage(f"\nlayer {plnumber} of zone {zonenumber} done")
                            
                            #solidsforlist.append(solids)
                            if solids:
                                for solid in solids:
                                    solidsforlist.append(str(t.GetObject(solid,OpenMode.ForWrite).Handle))
                                    propertyforlist.append(zonenumber)
                        else:
                            # If boundary fails, try alternative approach
                            editor.WriteMessage(f"\nsurface has no area, invalid boundary")
                            pass     
                        
                        surface.BreaklinesDefinition.RemoveAt(0)
                        surface.Rebuild()
                        # Delete surface
                        plineobj.Erase(True)
                        triangles.Dispose()
                        point_collection.Clear()
                        point_collection.Dispose()
                        
                        plnumber += 1
                        
                
                surface.Erase(True)
                    
                t.Commit()
                            
                solidlistout.append(solidsforlist) 
                propertiesout.append(propertyforlist)      
                
                gc.collect()
                 
OUT = solidlistout, propertiesout

@kovacsv the Dynamo team has already spent a lot of time trying to measure the performance issue with Curve.ExtrudeAsSolid, but cannot make any progress with optimization unless we have more data or fake data to reproduce this. If you can share the point data or the curve data, save it as a SAT file and share the SAT with us, that too will help a lot. That will eliminate the dependency on Civil3D as well, as if there’s anything specific to C3D/AutoCAD, that again needs to be known by us so we can involve engineers from those teams to help.