Determining Purgeable Elements

A question that crops up from time to time and is rather hard to answer exactly and exhaustively is on purgeable elements:

Question: What is the best way to get all of the elements that can be purged? I can use WhereElementIsElementType filter to return all types, and WhereElementISNotElementType to return instances, but comparing the two lists does not leave me with the purgeable result. I could compare individual lists of types (i.e. family symbol and family instance), but that seems prone to error. Is there any good way to get this information out of the model?

Answer: Yes, the Revit API does not currently provide any direct method to determine this.

Let’s see what can be done about it anyway already.

When you say “all of the elements that can be purged”, that can be a pretty huge task.

I would suggest breaking that down into individual sub-tasks for different types of purgeable elements, which will require pretty different handling.

For example, I implemented a method DeleteUnnamedNonHostingReferencePlanes to find and

delete all reference planes not hosting any elements
.

An explicit reference to the term purge is used in the post on

removing unused text note types
.

One way to determine whether a certain element A is required by another one B is to delete A and check the return value of the Delete method, which provides a list of deleted element ids. In some cases, B is deleted as well, and its element id is included the resulting list of deleted element ids. This is used by the

object relationship analyser

(VB).

The deletion can be made temporary by encapsulating it in a transaction that is later rolled back. Here is a discussion of

the temporary transaction trick
including
a list of numerous usage examples, and later

touched up slightly
.

Unfortunately, of course, this method becomes extremely slow in a large model with each deletion taking place in its own transaction and then rolled back.
Wrapping it all in one single transaction will essentially deleting the entire model, which causes other problems is also not very useful, so this idea may not really be very feasible for the task at hand.

When you do have access to a one-way relationship between two classes of objects, e.g. pointers from doors, windows and openings hosted in walls to their respective wall host elements, you can easily invert the relationship to determine the list of hosted object for each host. This is demonstrated in my discussion of the

relationship inverter
in
one of the very first blog posts.

Taking a look at family types only, which is a small subset of the potentially purgeable elements, a similar one-way relationship as the one between hosted objects and their hosts is defined between non-element types and types, since the former provide a property named GetTypeId pointing to the type. You could make use of this in a similar manner to find all unused types as follows:

  1. Determine all non-type elements using WhereElementISNotElementType
  2. Iterate over all non-type elements. Determine the element id of each one, and build a dictionary D of the inverse relationship, mapping type element id to a list or the elements using that type.
  3. Use WhereElementIsElementType to determine all element types. Iterate over them. For each type, check D to see how many elements are referencing it. If D has no entry for this type, it is not being used and can be deleted.

There is no guarantee that this will really work, though, since there may be any number of exceptional situations that need to be handled differently on a case-by-case base.

You can try it out, though, see whether you corrupt the database in any way, test thoroughly, possibly add handling of all special cases that you encounter, and maybe achieve your goal in the long run.

Unfortunately, there is currently no API access to the list of objects or categories searched by the built-in purge functionality, and the logic used for it is non-trivial.

Definitely, a number of special cases need to be handled, and there are some things that cannot be determined using the method described above.

For example, there are no instance placements of materials, so you have to search through instances placed in the model and for each element determine whether the material is used by it or not.
This also leaves out materials that are set for type parameters on types that have not been used but are loaded into the model.


Comments

5 responses to “Determining Purgeable Elements”

  1. Arnošt Löbel Avatar
    Arnošt Löbel

    Actually, the best way to approach purging is to ask the Revit Development Team for providing a Purging API. If enough customers ask for it, it will be added, eventually. In the meantime various techniques can be used, but none can do exactly what Revit does. There is a danger in purging anything that appears unused, because often unused types and other elements need to be present in order for Revit function correctly (e.g. when certain edit modes or sketches start). Removing those essential elements could lead to undesirable problems.
    Arnošt Löbel
    Autodesk Revit R&D

  2. Amen!
    Where can I send a request? I totally agree that replicating native purging functionality gets cumbersome. Purging API would be a great addition, because a lot of people write custom and very specific to their company tools to exchange / prepare models for sharing and purging is ALWAYS a big part of this procedure!

  3. Dear Dima,
    If it can be simply described using text only, you can just submit a comment requesting it right here.
    Slightly more officially, you can submit it to an Autodesk discussion forum.
    For a totally official one, you can submit an ADN DevHelp case, if you are a member.
    AUGI also manages a wishlist which is taken very seriously by Autodesk.
    If it not possible or easy to describe you request in just simple text, you should create a more detailed description, possibly including images, sample code, a minimal sample model etc. There are various places you can post such material on the Internet and include a link to it in your submission.
    We often need a reproducible case to discover an error, i.e.
    – Detailed step-by-step instructions for reproducing the issue.
    – Specification of the exact behaviour you observe versus what you expect.
    – A complete yet minimal Revit sample model to run a test in.
    – A complete yet minimal Visual Studio solution with add-in manifest so that we can compile, load and run the application with a single click and explore its behaviour live in the sample model you provide.
    I hope this helps.
    Cheers, Jeremy.

  4. frank halliday Avatar
    frank halliday

    Hi Jeremmy,
    could you explain what this…
    doc.GetUnusedFamilies()
    is it a return reference for a list of ElementIds that can be purged? If so how can it be an extension method that is not recognised by the Revit API and yet grabs the Document doc handle? I am a little confused.
    many thanx fabs
    HashSet hashSet = new HashSet();
    using (IEnumerator enumerator = ((IEnumerable) doc.GetUnusedAppearances()).GetEnumerator())
    {
    while (((IEnumerator) enumerator).MoveNext())
    {
    ElementId current = enumerator.Current;
    hashSet.Add(current);
    }
    }
    using (IEnumerator enumerator = ((IEnumerable) doc.GetUnusedMaterials()).GetEnumerator())
    {
    while (((IEnumerator) enumerator).MoveNext())
    {
    ElementId current = enumerator.Current;
    hashSet.Add(current);
    }
    }
    using (IEnumerator enumerator = ((IEnumerable) doc.GetUnusedFamilies()).GetEnumerator())
    {
    while (((IEnumerator) enumerator).MoveNext())
    {
    ElementId current = enumerator.Current;
    hashSet.Add(current);
    }
    }

  5. Dear Frank,
    No idea whatsoever.
    Where did you get that from?
    What gave you the idea to ask me?
    It does indeed look like a couple of extension methods that somebody added to the Document class.
    It also looks a little bit old-fashioned…
    You do know what extension methods are, don’t you?
    Anybody can create one, on any class, e.g. like this:
    http://thebuildingcoder.typepad.com/blog/2010/02/getpolygon-extension-methods.html
    Here is a rather more recent example:
    http://thebuildingcoder.typepad.com/blog/2014/12/selfilter-a-powerful-generic-selection-filter-utility.html
    Cheers, Jeremy.

Leave a Reply to Dima ChiriacovCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading