Committing Transactions when creating a large number of Elements

Committing Transactions when creating a large number of Elements

ewano
Advocate Advocate
3,915 Views
14 Replies
Message 1 of 15

Committing Transactions when creating a large number of Elements

ewano
Advocate
Advocate

Hi All,

 

I am just wondering if there is a Transaction limit when it comes to the number of Revit elements that can be created via the API.

 

I am using Dynamo Python to create,

collect = Autodesk.Revit.Creation.FamilyInstanceCreationData

and use it with the,

items = doc.Create.NewFamilyInstances2(collect)

with the purpose of placing approximately 10,000 Revit families (the family size is 1mb)

 

Code below:

 

 

## Import Reference Examples

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import *

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

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


clr.AddReference('System')
from System.Collections.Generic import List as sList

clr.AddReference('DSCoreNodes') 
import DSCore
from DSCore import *


def flat(lst):
	return DSCore.List.Flatten(lst,2)
	
## Split list into chuncks
def chunks(data,sizes):
	it = iter(data)
	return [[next(it) for _ in range(size)] for size in sizes]

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument

location_list = flat(IN[0])
family_list = flat(IN[1])
rotation_list = flat(IN[2])
lev_list = flat(IN[3])

result = []

collect = sList[Autodesk.Revit.Creation.FamilyInstanceCreationData]()
	
for location, family, rotation, lev in  zip(location_list,family_list,rotation_list,lev_list):

	location1 = location.ToXyz()
	family1 = UnwrapElement(family)
	rotation1 = Math.DegreesToRadians(rotation)
	lev1 = UnwrapElement(lev)
	
	create = Autodesk.Revit.Creation.FamilyInstanceCreationData(location1,family1,lev1,StructuralType.NonStructural)
	create.RotateAngle = rotation1
	create.Axis = Line.ByStartPointDirectionLength(location,Vector.ZAxis(),1).ToRevitType()
	
	collect.Add(create)

# Start
TransactionManager.Instance.EnsureInTransaction(doc)

items = doc.Create.NewFamilyInstances2(collect)
	
# Stop
TransactionManager.Instance.TransactionTaskDone()

elems = []

for i in items:
	elems.append(doc.GetElement(i))

counts = IN[4]

collect.Clear()

OUT = chunks(elems,counts)

 

 

 

I know this code works to place 100 Revit Families, but my current test is taking a very long time to do 10,000 ish.

Revit has been displaying the 'Committing Transaction' message in the bottom task bar for 10 hours now. The process of creating 100 Revit Families takes approximately 6 minutes, so I am wondering if there is a limit to the number of Revit Elements that can be rolled into being created in 1 Transaction within the API.

 

Any thoughts or advice?

 

I guess I could split the creation data into chunks of 100 and commit for each chunk...

 

Cheers,

Ewan

 

ADSKEE.png

 

 

 

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes
Accepted solutions (1)
3,916 Views
14 Replies
Replies (14)
Message 2 of 15

ricaun
Advisor
Advisor

100 families equal to 6 minutes, which looks terrible 😥

 

I'm not sure if has a limit of elements or changes in the document when committing a transaction.

 

Pretty sure that does not scale well, with 1000 families you gonna have 1h for sure, and so on.

 

After the commit, the document is Regenerated and if you add a Transaction for each one probably you gonna have a bigger time.

 

Your family size looks normal but I supposed a smaller family gonna be faster (Need to make some tests to make sure.)

 

Make some chunks like you said, every 100 families commit the transaction and notify the user. If was C# I gonna say to use a simple progress bar, but in dynamo, I don't know if that is similar.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 3 of 15

Mert_Yacan
Contributor
Contributor

I would try using the NewFamilyInstance function instead of NewFamilyInstance2. I would also do the creation inside the for loop you have, instead of filling the data from one list into another list. 

Message 4 of 15

ewano
Advocate
Advocate

Thanks for the thoughts @ricaun 

 

The 6 minute runtime also took into account the setting of a multitude of parameter values after the families had been placed, and was a vast improvement on the previous workflow timing.

 

I have just re-executed the code without the Transaction, to assess exactly how many Revit Families were going to be placed. 28,215, so a little over the 10,000 I thought 😅

 

I will go look at splitting the creation process to run over 100 elements per Transaction for a sample set of 500 elements, just to start to assess if that is even an option for making progress. And as part of that, change the family type to a simple testing container with limited geometric and parametric data.

 

Will post back on how I get on 🤞

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

Message 5 of 15

ewano
Advocate
Advocate

@Mert_Yacan Also good points, will have a look after I try a sample set run.

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes
Message 6 of 15

ewano
Advocate
Advocate

100 Simple data container families (3d boxes about 200mm sq with some parameters, not set)

  • 3 min 30 seconds using the NewFamilyInstances2 method outside of the loop with a list of combined CreationData.
  •  2 min  55 seconds using the NewFamilyInstances2 method outside of the loop with individual CreationData.

I have remembered why I needed to use this method (instead of NewFamilyInstance) and it was because of the ability to set the rotation and axis as part of the creation process, and not have to adjust it after, which I think I did test and was very time intensive.

 

 

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes
Message 7 of 15

ewano
Advocate
Advocate

Also looking into the performance timing of rotating placed families after they are created, instead of incorporating this into the creation data. Will post back with some insight on that as well. I've had a Dynamo forum user confirm the method used can create 20,000 elements in about 15 minutes, so something is definitely up with my data. 🤔

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes
Message 8 of 15

jeremy_tammik
Alumni
Alumni

I have some good news for you as well, to underline the Dynamo forum confirmation:

  

NewFamilyInstances2 Can Create 27000 Instances

  

https://thebuildingcoder.typepad.com/blog/2022/08/ifc-property-set-bulk-instances-and-vary-by-group....

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 9 of 15

ewano
Advocate
Advocate

Thanks @jeremy_tammik

This undertaking is part of memory testing for a large automation procedure so finding where the limitations are is key.

 

I will have a read of your posting in the morning, getting late here...

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes
Message 10 of 15

ewano
Advocate
Advocate

I performed a quick test this morning to check how the family I am using stacks up against a generic family that only contains a 200mm Cube. I removed my pre-creation code (logic to work out point locations) and used generic points for creation.

 

Timings as below:

 

My Family:

Placed 1,000 instances in 30 seconds

Placed 5,000 instances in 13 minutes

 

200mm Cube:

Placed 1,000 instances in 3 seconds

Placed 5,000 instances in 5 seconds

Placed 10,000 instances in 12 seconds

Placed 50,000 instances in 9 minutes

 

I have some more batch testing to try splitting my data into 'chunks' for each transaction, and I am hopeful this will help both regeneration and timings. More soon.

 

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

Message 11 of 15

ewano
Advocate
Advocate

I think my family creation time using the FamilyInstances2 method will work overall, I am confident that batch execution using it will work. Just having a head scratcher with the below. Ideally it would run a Transaction for each chunk ignoring any warnings thrown.

 

Bit of a long one… but here goes.

 

So Code 1 will process everything as chunks but commit 1 transaction (I’m looking for a transaction per chunk) with no warnings, creates elements as expected. Some DuplicateInstances warnings are created for multiple elements, which is ok as I’m looking on how to ignore this programmatically.

 

Code 1

## Import Reference Examples

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import *

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

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


clr.AddReference('System')
from System.Collections.Generic import List as sList

clr.AddReference('DSCoreNodes') 
import DSCore
from DSCore import *


def flat(lst):
	return DSCore.List.Flatten(lst,2)

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument

location_list = flat(IN[0])
family_list = flat(IN[1])
rotation_list = flat(IN[2])
lev_list = flat(IN[3])

counts = IN[4]

## Split list into chuncks
def chunks(data,sizes):
	it = iter(data)
	return [[next(it) for _ in range(size)] for size in sizes]

chopLen = IN[5]

# Yield successive n-sized chunks from L.
def chop(L, n):
	for i in range(0, len(L), n):
		yield L[i:i+n]
        
# # Chop Inputs
chopLocation = chop(location_list,chopLen)
chopFamily = chop(family_list,chopLen)
chopRotation = chop(rotation_list,chopLen)
chopLevel = chop(lev_list,chopLen)

#OUT = chopLocation

class DuplicateInstancesWarningSwallower(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        fail_list = List[FailureMessageAccessor]()
        fail_acc_list = failuresAccessor.GetFailureMessages().GetEnumerator()
           
        for failure in fail_acc_list:
            failuresAccessor.ResolveFailure(failure)
            failure_id = failure.GetFailureDefinitionId()
            failure_severity = failure.GetSeverity()
            failure_type = BuiltInFailures.InaccurateFailures.DuplicateInstances
            if failure_id == failure_type:
                
                failuresAccessor.DeleteWarning(failure)

        return FailureProcessingResult.Continue

output = []
result = []
testing = []


collect = sList[Autodesk.Revit.Creation.FamilyInstanceCreationData]()

for cLocation, cFamily, cRotation, cLev in  zip(chopLocation,chopFamily,chopRotation,chopLevel):
	
	collect.Clear()

	TransactionManager.Instance.EnsureInTransaction(doc)
	
	for location, family, rotation, lev in  zip(cLocation, cFamily, cRotation, cLev):		
	
		location1 = location.ToXyz()
		family1 = UnwrapElement(family)
		rotation1 = Math.DegreesToRadians(rotation)
		lev1 = UnwrapElement(lev)
	
		create = Autodesk.Revit.Creation.FamilyInstanceCreationData(location1,family1,lev1,StructuralType.NonStructural)
		create.RotateAngle = rotation1
		create.Axis = Line.ByStartPointDirectionLength(location,Vector.ZAxis(),1).ToRevitType()
		
		collect.Add(create)
		
	items = doc.Create.NewFamilyInstances2(collect)	
	
	pack = []
	for i in items:
		pack.append(doc.GetElement(i))
	result.append(pack)

	TransactionManager.Instance.TransactionTaskDone()


OUT = result

Code 2 will process chunks as separate Transactions but throws the DuplicateInstances warnings when run, so not ideal for a ‘hands-free’ user execution. Resolution by the 'OK' button is the option I want to go with.

 

ewano_1-1669604152142.png

 

 

Code 2

## Import Reference Examples

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import *

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

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


clr.AddReference('System')
from System.Collections.Generic import List as sList

clr.AddReference('DSCoreNodes') 
import DSCore
from DSCore import *


def flat(lst):
	return DSCore.List.Flatten(lst,2)

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument

location_list = flat(IN[0])
family_list = flat(IN[1])
rotation_list = flat(IN[2])
lev_list = flat(IN[3])

counts = IN[4]

## Split list into chuncks
def chunks(data,sizes):
	it = iter(data)
	return [[next(it) for _ in range(size)] for size in sizes]

chopLen = IN[5]

# Yield successive n-sized chunks from L.
def chop(L, n):
	for i in range(0, len(L), n):
		yield L[i:i+n]
        
# # Chop Inputs
chopLocation = chop(location_list,chopLen)
chopFamily = chop(family_list,chopLen)
chopRotation = chop(rotation_list,chopLen)
chopLevel = chop(lev_list,chopLen)

#OUT = chopLocation

class DuplicateInstancesWarningSwallower(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        fail_list = List[FailureMessageAccessor]()
        fail_acc_list = failuresAccessor.GetFailureMessages().GetEnumerator()
           
        for failure in fail_acc_list:
            failuresAccessor.ResolveFailure(failure)
            failure_id = failure.GetFailureDefinitionId()
            failure_severity = failure.GetSeverity()
            failure_type = BuiltInFailures.InaccurateFailures.DuplicateInstances
            if failure_id == failure_type:
                
                failuresAccessor.DeleteWarning(failure)

        return FailureProcessingResult.Continue

output = []
result = []
testing = []


collect = sList[Autodesk.Revit.Creation.FamilyInstanceCreationData]()

for cLocation, cFamily, cRotation, cLev in  zip(chopLocation,chopFamily,chopRotation,chopLevel):
	
	collect.Clear()
	
	TransactionManager.Instance.ForceCloseTransaction()
	
	
	t = Transaction(doc, "Batch Create Families")
	
	# Start
	t.Start()
	
	#tOpt = t.GetFailureHandlingOptions()
	#tOpt.SetFailuresPreprocessor(DuplicateInstancesWarningSwallower())
	#t.SetFailureHandlingOptions(tOpt)
	
	for location, family, rotation, lev in  zip(cLocation, cFamily, cRotation, cLev):		
	
		location1 = location.ToXyz()
		family1 = UnwrapElement(family)
		rotation1 = Math.DegreesToRadians(rotation)
		lev1 = UnwrapElement(lev)
	
		create = Autodesk.Revit.Creation.FamilyInstanceCreationData(location1,family1,lev1,StructuralType.NonStructural)
		create.RotateAngle = rotation1
		create.Axis = Line.ByStartPointDirectionLength(location,Vector.ZAxis(),1).ToRevitType()
		
					
		collect.Add(create)
		
	items = doc.Create.NewFamilyInstances2(collect)	
	
	
	pack = []
	for i in items:
		pack.append(doc.GetElement(i))
	result.append(pack)

	t.Commit()

OUT = result

And Code 3 enables FailurePreProcessor methods, shows no warning but creates nothing…

 

Code 3

## Import Reference Examples

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import *

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

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


clr.AddReference('System')
from System.Collections.Generic import List as sList

clr.AddReference('DSCoreNodes') 
import DSCore
from DSCore import *


def flat(lst):
	return DSCore.List.Flatten(lst,2)

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument

location_list = flat(IN[0])
family_list = flat(IN[1])
rotation_list = flat(IN[2])
lev_list = flat(IN[3])

counts = IN[4]

## Split list into chuncks
def chunks(data,sizes):
	it = iter(data)
	return [[next(it) for _ in range(size)] for size in sizes]

chopLen = IN[5]

# Yield successive n-sized chunks from L.
def chop(L, n):
	for i in range(0, len(L), n):
		yield L[i:i+n]
        
# # Chop Inputs
chopLocation = chop(location_list,chopLen)
chopFamily = chop(family_list,chopLen)
chopRotation = chop(rotation_list,chopLen)
chopLevel = chop(lev_list,chopLen)

#OUT = chopLocation

class DuplicateInstancesWarningSwallower(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        fail_list = List[FailureMessageAccessor]()
        fail_acc_list = failuresAccessor.GetFailureMessages().GetEnumerator()
           
        for failure in fail_acc_list:
            failuresAccessor.ResolveFailure(failure)
            failure_id = failure.GetFailureDefinitionId()
            failure_severity = failure.GetSeverity()
            failure_type = BuiltInFailures.InaccurateFailures.DuplicateInstances
            if failure_id == failure_type:
                
                failuresAccessor.DeleteWarning(failure)

        return FailureProcessingResult.Continue

output = []
result = []
testing = []


collect = sList[Autodesk.Revit.Creation.FamilyInstanceCreationData]()

for cLocation, cFamily, cRotation, cLev in  zip(chopLocation,chopFamily,chopRotation,chopLevel):
	
	collect.Clear()
	
	TransactionManager.Instance.ForceCloseTransaction()
	
	
	t = Transaction(doc, "Batch Create Families")
	
	# Start
	t.Start()
	
	tOpt = t.GetFailureHandlingOptions()
	tOpt.SetFailuresPreprocessor(DuplicateInstancesWarningSwallower())
	t.SetFailureHandlingOptions(tOpt)
	
	for location, family, rotation, lev in  zip(cLocation, cFamily, cRotation, cLev):		
	
		location1 = location.ToXyz()
		family1 = UnwrapElement(family)
		rotation1 = Math.DegreesToRadians(rotation)
		lev1 = UnwrapElement(lev)
	
		create = Autodesk.Revit.Creation.FamilyInstanceCreationData(location1,family1,lev1,StructuralType.NonStructural)
		create.RotateAngle = rotation1
		create.Axis = Line.ByStartPointDirectionLength(location,Vector.ZAxis(),1).ToRevitType()
		
					
		collect.Add(create)
		
	items = doc.Create.NewFamilyInstances2(collect)	
	
	
	pack = []
	for i in items:
		pack.append(doc.GetElement(i))
	result.append(pack)

	t.Commit()

OUT = result

At least I am making progress

ewano_0-1669603533871.png

 

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

Message 12 of 15

ewano
Advocate
Advocate

Well testing is going OK. 120 data chunks (100 individual CreationData elements in each)  resulted in 3692 elements being placed in 162 minutes. 😬

 

That was 120 separate transactions though. @jeremy_tammik Is there a guide limit on the number of transactions that should be committed before performance is affected? Should these be bundled into a TransactionGroup and if so, what are the performance benefits really going to be?

 

Still looking into saving all pre-creation data to JSON to enable smaller sequential creation runs and better cross process data usage.

 

I now know once the function has run, the file should auto-save if all commits are successful, instead of regenerating the view (which is what I have just done am still waiting for Revit to unfreeze) 😅

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes
Message 13 of 15

jeremy_tammik
Alumni
Alumni

Congratulations on making good progress!

  

I am afraid I cannot provide much guidance here; I think you are at the forefront of research in this area now.

 

I believe the answers to your questions are unknown, or depend so heavily on the implementation details that they vary from case to case. I suggest benchmarking, researching, and would be very grateful to hear your results.

 

> Is there a guide limit on the number of transactions that should be committed before performance is affected?

 

I am not aware of any.

 

> Should these be bundled into a TransactionGroup?

 

Add-ins often do. Probably more for clarify's sake than performance.

 

> What are the performance benefits really going to be?

 

Unknown, possibly nil.

 

> Still looking into saving all pre-creation data to JSON to enable smaller sequential creation runs and better cross process data usage.

  

Yes, that sounds like a good idea.

  

> I now know once the function has run, the file should auto-save if all commits are successful, instead of regenerating the view (which is what I have just done am still waiting for Revit to unfreeze)

  

Very interesting.

  

I would recommend maintaining a detailed log of the process as it proceeds to help keep track of where you are. Also, monitor the Revit process health from outside, e.g., by checking that the log file is being updated. If Revit crashes, handle that gracefully and implement a possibility to restart and continue processing at the last known point.

  

The Building Coder occasionally mentioned some aspects of batch processing that might be of interest:

  

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 14 of 15

ewano
Advocate
Advocate

"I think you are at the forefront of research in this area now."

Good to know 😅

 

An update on testing. It would appear from large scale testing that Revit holding lots of Transactions in the 'Undo' stack may be causing a significant decrease in performance. Reducing these through TransactionGroups was something I wanted to try.

 

I have used a sample Transaction of placing a single Point based Foundation Pad element to compare whether using TransactionGroups provides any performance gains.

 

Result 1 - Small Scale:

10 Individual Transactions = 7.3 seconds

1 TransactionGroup  containing the same 10 Transactions = 1.4 seconds

 

This trend of improving the runtime is mirrored in larger scale testing.

 

Result 2 - Large Scale:

10 Transactions each creating 100 elements = 202 seconds

1 TransactionGroup containing 10 Transactions, each creating 100 elements = 44 seconds

 

Result 3 - Larger Scale in Project:

I tested again inside my project environment with good results.

Yesterday = 1200 CreationData entries, 3692 Revit elements created, 160 minutes.

Today = 8000 CreationData entries, 8336 Revit Elements created, 160 seconds...

 

I'm doing something right 😎

 

I have also implemented an IFailuresPreprocessor, that now works properly to ignore/remove the 'DuplicateInstances' warning. So less warnings for Revit to keep track of as well.

 

Revit is performing much better now after the entire creation process has completed. So I am happy with that. 🙂

 

More soon 👍

 

 

 

 

 

 

 

 

 

 

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

Message 15 of 15

ewano
Advocate
Advocate
Accepted solution

Hi All,

 

I have found a solution that I am happy with. 😃

 

Using a Transaction Group to process groups of 100 Creation data elements as 'SubTransactions' then Assimilating them for a single commit.

 

Each SubTransaction takes 1.1 seconds to process based on what the Revit Journal File is telling me. 🤔

This timing scales fine for my application, as I have fixed the duplicate element errors that were occurring at this stage in my process.

 

Doc.Regeneration takes a little while after my process has been run, but I cannot see a way of reducing that time.

(Not that its too long, only takes a couple of minutes after the process 'says' it has been completed in my log file.)

 

Here is the code for reference, thanks all for your input.

 

## Import Reference Examples

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

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Events import DialogBoxShowingEventArgs

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

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


clr.AddReference('System')
from System.Collections.Generic import List as sList

clr.AddReference('DSCoreNodes') 
import DSCore
from DSCore import *

def flat(lst):
	return DSCore.List.Flatten(lst,2)

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument

location_list = flat(IN[0])
family_list = flat(IN[1])
rotation_list = flat(IN[2])
lev_list = flat(IN[3])

counts = IN[4]

## Split list into chuncks
def chunks(data,sizes):
	it = iter(data)
	return [[next(it) for _ in range(size)] for size in sizes]

chopLen = IN[5]

# Yield successive n-sized chunks from L.
def chop(L, n):
	for i in range(0, len(L), n):
		yield L[i:i+n]
        
# # Chop Inputs
chopLocation = chop(location_list,chopLen)
chopFamily = chop(family_list,chopLen)
chopRotation = chop(rotation_list,chopLen)
chopLevel = chop(lev_list,chopLen)

# Failure Info
class CreateFailureAdvancedHandler(IFailuresPreprocessor):
	def PreprocessFailures(self, failuresAccessor):
		failMessages = failuresAccessor.GetFailureMessages()
		if failMessages.Count == 0:
			return FailureProcessingResult.Continue
		if failuresAccessor.GetSeverity() == FailureSeverity.Warning:
			for currentMessage in failMessages:
				failuresAccessor.DeleteWarning(currentMessage)	
		return FailureProcessingResult.Continue
		
# Outputs

result = []
testing = []

# Family Instance Creation Data
collect = sList[Autodesk.Revit.Creation.FamilyInstanceCreationData]()

TransactionManager.Instance.ForceCloseTransaction()

tg = TransactionGroup(doc,"Multiple Family Creation")
tg.Start()

for cLocation, cFamily, cRotation, cLev in  zip(chopLocation,chopFamily,chopRotation,chopLevel):
	
	collect.Clear()
	
	# Start the sub-transaction
	t = Transaction(doc, "SubTransaction")
	t.Start()
	
	# Set how Failures will be handled fore the Transaction
	failureOptions = t.GetFailureHandlingOptions()
	failureOptions.SetFailuresPreprocessor(CreateFailureAdvancedHandler())
	t.SetFailureHandlingOptions(failureOptions)
	
	
	for location, family, rotation, lev in  zip(cLocation, cFamily, cRotation, cLev):		
	
		location1 = location.ToXyz()
		family1 = UnwrapElement(family)
		rotation1 = Math.DegreesToRadians(rotation)
		lev1 = UnwrapElement(lev)
	
		create = Autodesk.Revit.Creation.FamilyInstanceCreationData(location1,family1,lev1,StructuralType.NonStructural)
		create.RotateAngle = rotation1
		create.Axis = Line.ByStartPointDirectionLength(location,Vector.ZAxis(),1).ToRevitType()
		
		collect.Add(create)
	
	# Place DB FamilyInstances
	items = doc.Create.NewFamilyInstances2(collect)	

	# Commit Transaction
	t.Commit()
	
	# Get Revit Families	
	pack = []
	for i in items:
		pack.append(doc.GetElement(i))
	result.append(pack)

# Combine all the SubTransactions into one Undo Stack item	
tg.Assimilate()

## Post Process Flattened Lists to match input structure
resultF = flat(result)
output = chunks(resultF,counts)
OUT = output

 

 

Cheers,


Ewan



Autodesk ITF 2018-2020 / Senior Project Drafter / Technical & Computational Modelling Specialist / Dynamo Enthusiast

0 Likes