Optimization of Transaction Times for Room Boundary Line Creation in Revit

Optimization of Transaction Times for Room Boundary Line Creation in Revit

veeresh.huvinahalliACK3L
Enthusiast Enthusiast
623 Views
7 Replies
Message 1 of 8

Optimization of Transaction Times for Room Boundary Line Creation in Revit

veeresh.huvinahalliACK3L
Enthusiast
Enthusiast

I am currently working on creating room boundary lines in Revit, and I’ve encountered significant delays in the transaction process. Here are the specifics of my situation:

  • I have approximately 5,000 walls from which I’m deriving curves to create the room boundaries.
  • Initially, I attempted to create all lines in a single transaction, but the transaction.Commit method took over 45 minutes to complete.
  • I then modified my approach to commit in batches of 500, which reduced the time to about 35-40 minutes.
  • Additionally, I implemented document.Regenerate before committing the transaction, but it still takes around 30-35 minutes.

Could anyone suggest strategies or best practices to further optimize the transaction times? Are there specific methods or techniques within Revit's API that could help streamline this process? Any insights or recommendations would be greatly appreciated.

0 Likes
Accepted solutions (2)
624 Views
7 Replies
Replies (7)
Message 2 of 8

Speed_CAD
Collaborator
Collaborator

Hi,

 

Regenerating the document may be necessary for certain approaches, I imagine that when you regenerate the document you do it outside the loop.

 

Using batch transactions is a good idea, you could try reducing the batch to 200 or 300 and try it out.

 

Another idea is to encapsulate the transactions in a TransactionGroup, this helps to manage the transactions in a more efficient and orderly way.

 

If you are using some kind of event that is triggered when you are adding elements, the ideal would be to disable those events temporarily and restore them when the transaction is confirmed.

 

You could also try to optimize the obtaining of curves, for example, eliminate redundant curves or check if some can be combined before using them in the transaction.

 

Anyway, without seeing part of your code it is difficult for them to help you. If you can, it would be good to see part of the code to see if it is possible to optimize it or if there is definitely nothing that can be done.

Mauricio Jorquera
0 Likes
Message 3 of 8

veeresh.huvinahalliACK3L
Enthusiast
Enthusiast

private List<ElementId> UnjoinedWallElements(ViewPlan viewPlan, List<Element> tyWallElements, ElementId levelId, List<Element> corridorWalls)
{
var roomIds = new List<ElementId>();
const int batchSize = 300; // Process 500 walls at a time

for (int i = 0; i < tyWallElements.Count; i += batchSize)
{
// Create a sublist of walls for the current batch
var batch = tyWallElements.Skip(i).Take(batchSize).ToList();

using (Transaction transElem = new Transaction(document.Document))
{
transElem.Start("CreateLine");

setFailureDialogSuppressor(transElem);

foreach (Wall wall in batch)
{
if (wall.IsValidObject)
{
LocationCurve locCurve = wall.Location as LocationCurve;
if (locCurve != null)
{
Curve curve = locCurve.Curve;
if (curve != null && curve.IsBound)
{
// Create a CurveArray and add the wall's curve to it
CurveArray curveArray = new CurveArray();
curveArray.Append(curve);

bool shortLength = false;

foreach (Curve curve1 in curveArray)
{
// Get the length of the curve
double curveLength = curve1.Length;

// Compare the length with the threshold
if (curveLength <= 0.0025602645572916664)
{
shortLength = true;
}
}

if (shortLength)
continue;

// Find the appropriate level for the sketch plane
Level level = document.Document.GetElement(wall.LevelId) as Level;
if (level != null)
{
SketchPlane sp = SketchPlane.Create(document.Document, level.Id);
// Create room boundary lines
ModelCurveArray out1 = document.Document.Create.NewRoomBoundaryLines(sp, curveArray, viewPlan);
if (corridorWalls.Any(x => x.Id.Equals(wall.Id)))
roomIds.Add(out1.get_Item(0).Id);

document.Document.Regenerate();
}
}
}
}
}

document.Document.Regenerate();
Log.Information($"Batch processing from {i} to {i + batch.Count - 1} started");
transElem.Commit();
Log.Information($"Batch processing from {i} to {i + batch.Count - 1} completed");
}
}

return roomIds;
}

Code snippet

 

0 Likes
Message 4 of 8

Speed_CAD
Collaborator
Collaborator

...

Mauricio Jorquera
Message 5 of 8

veeresh.huvinahalliACK3L
Enthusiast
Enthusiast

@Speed_CAD Thank you for the reply sir.

But still its taking lot amount of time. In the previous approach it committed transaction after every batch of 300 but now we are commit the transaction only once previously I had these same approach, so these won't work.

I'm sharing the log from when commit started and when it completed I'm creating 5330 roomboundary lines.

2024-09-29 14:27:58.310 +05:30 [INF] Creating Room Line Started 5330
2024-09-29 15:12:54.056 +05:30 [INF] Done Room Line Placement

0 Likes
Message 6 of 8

jeremy_tammik
Alumni
Alumni

One thing that can be easily optimised is to reuse the existing sketch planes instead of creating a new sketch plane for each room boundary. Surely you do not have every single room on a separate level? Have you benchmarked the room boundary line creation separately from the sketch plane creation? Maybe half your time is spent creating thousands of identical sketch planes? If your room boundaries reside on a level, I believe there is normally a sketch plane associated with the level, so you could use that for the boundary lines as well.

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 7 of 8

Mohamed_Arshad
Advisor
Advisor
Accepted solution

Hi @veeresh.huvinahalliACK3L 

 

   I have optimized little bit by removing some redundant code. And I have updated the Plane creation from Level, because sometimes wall can be offset from the level. In such case room boundary will be placed in wrong manner. Kindly check the below code for additional reference.

 

Reference Code Snippet

  private List<ElementId> UnjoinedWallElements(ViewPlan viewPlan, List<Element> tyWallElements, ElementId levelId, List<Element> corridorWalls)
  {
      var roomIds = new List<ElementId>();
      const int batchSize = 300; // Process 500 walls at a time

      for (int i = 0; i < tyWallElements.Count; i += batchSize)
      {
          // Create a sublist of walls for the current batch
          var batch = tyWallElements.Skip(i).Take(batchSize);

          ///Get Enumerator
          IEnumerator<Element> batchEnum = batch.GetEnumerator();

          using (Transaction transElem = new Transaction(document.Document))
          {
              transElem.Start("CreateLine");

              setFailureDialogSuppressor(transElem);

              while (batchEnum.MoveNext()) ///Replaced with While loop
              {
                  if (batchEnum.Current.IsValidObject)
                  {
                      using (LocationCurve locCurve = batchEnum.Current.Location as LocationCurve)
                      {
                          if (locCurve != null)
                          {
                              Curve curve = locCurve.Curve;

                              if (curve != null && curve.IsBound && curve.Length >= CurveTolerance)
                              {
                                  // Create a CurveArray and add the wall's curve to it
                                  CurveArray curveArray = new CurveArray();
                                  curveArray.Append(curve);

                                  // Create Plane
                                  Plane plane = GetPlane(curve);

                                  if (plane != null)
                                  {
                                      SketchPlane sp = SketchPlane.Create(document.Document, plane);

                                      // Create room boundary lines
                                      ModelCurveArray out1 = document.Document.Create.NewRoomBoundaryLines(sp, curveArray, viewPlan);
                                      if (corridorWalls.Any(x => x.Id.Equals(batchEnum.Current.Id)))
                                          roomIds.Add(out1.get_Item(0).Id);

                                      document.Document.Regenerate();

                                      ///Memory Dispose
                                      curve.Dispose();
                                      curveArray.Dispose();
                                      plane.Dispose();
                                      sp.Dispose();
                                      out1.Dispose();
                                  }
                              }
                          }
                      }
                  }

              }

              document.Document.Regenerate();
              Log.Information($"Batch processing from {i} to {i + batch.Count() - 1} started");
              transElem.Commit();
              Log.Information($"Batch processing from {i} to {i + batch.Count() - 1} completed");
          }
      }

      return roomIds;
  }

 

Get Plane Method

 private Plane GetPlane(Curve curve)
 {

     XYZ dir = curve.GetEndPoint(1) - curve.GetEndPoint(1).Normalize();
     XYZ basis = XYZ.BasisZ;

     if(dir.IsAlmostEqualTo(basis)|| dir.IsAlmostEqualTo(-basis))
     {
         basis = XYZ.BasisX;
     }

     XYZ normal = dir.CrossProduct(basis);

     return Plane.CreateByNormalAndOrigin(normal, curve.GetEndPoint(0));
 }

 

Hope this will Helps 🙂


Mohamed Arshad K
Software Developer (CAD & BIM)

Message 8 of 8

jeremy_tammik
Alumni
Alumni
Accepted solution

This code will probably still create a large number of duplicate planes for 5000 walls. 

  

I suggest the following approach:

  

  • Implement a comparison operator for the Plane class
  • Using the comparison operator, implement a dictionary D of all the planes that you create, e.g., Dictionary<Plane,int>, where the plane is the key and the integer value can be anything you like, for instance, a count of how many times that specific plane has been reused
  • Before creating a new plane, check whether an identical one has already been created and is listed in D; if so, reuse the existing one instead of creating a new one

   

Here is the code for a comparison operator for Plane objects, c.f., The Building Coder sample Util class:

  

   

        public static int Compare(Plane a, Plane b)
        {
            var d = Compare(a.Normal, b.Normal);

            if (0 == d)
            {
                d = Compare(a.SignedDistanceTo(XYZ.Zero),
                    b.SignedDistanceTo(XYZ.Zero));

                if (0 == d)
                    d = Compare(a.XVec.AngleOnPlaneTo(
                        b.XVec, b.Normal), 0);
            }
            return d;
        }

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open