RBP, Materials, Assets and the Visual API

Two very different aspects of BIM materials, filtering for annotations and a powerful batch processor utility:

GetMaterialArea Behaviour Varies

GetMaterialArea returns different results for different kinds of elements, as explained in the Revit API discussion forum thread on the method GetMaterialArea appears to use different formulas for computing the area depending on the element category:

Question: How does the method GetMaterialArea compute the area depending on the element category? It appears to use different formulas for different categories. For a wall, it is the area of one of the side faces for a layer (which probably makes sense); for a window, however, the method returns the sum over all 6 faces (assuming a simple cuboid). Please provide documentation on how the method functions on different elements or a unified way of computing the area.

I also noticed that GetMaterialIds returns null for elements of (at least) these categories:

GetMaterialArea(matId) and GetMaterialVolume(matId) returns 0 for these categories.

Answer 1: You should check Category.HasMaterialQuantities for that, some categories don't support material quantities.

However, it is noted that such material quantities are calculated either from compound structure layers or geometry. Since the Window isn't a compound structure, area will be taken from faces of geometry I suspect; Wall, on the other hand, has compound structure. The geometry of the wall would at the same time be lacking the layers detail.

Answer 2: I think the customer is referring mostly to the difference between system families and loaded families in Revit itself. The API is probably returning the same values.

This is confirmed and workarounds are posted in the Revit Clinic article on Material Takeoff Area Schedule. Some other special cases are listed in the Autodesk Support article on Material Takeoff shows incorrect values of areas and/or volumes in Revit.

As far as the API goes, check out these 2 resources from The Building Coder and Scott Conover that may help confirm the API designed behaviour:

This is from Scott's presentation:

Material quantity extraction One common analytical requirement is to extract material quantities of elements in the document. Revit 2010 introduced methods to directly obtain the material volumes and areas computed by Revit for material takeoff schedules:

The methods apply to categories of elements where Category.HasMaterialQuantities property is true. In practice, this is limited to elements that use compound structure, like walls, roofs, floors, ceilings, a few other basic 3D elements like stairs, plus 3D families where materials can be assigned to geometry of the family, like windows, doors, columns, MEP equipment and fixtures, and generic model families. Note that within these categories there are further restrictions about how material quantities can be extracted. For example, curtain walls and curtain roofs will not report any material quantities themselves; the materials used by these constructs can be extracted from the individual panel elements that make up the curtain system. Note that the volumes and areas computed by Revit may be approximate in some cases. For example, for individual layers within a wall, minor discrepancies might appear between the volumes visible in the model and those shown in the material takeoff schedule. These discrepancies tend to occur when you use the wall sweep tool to add a sweep or a reveal to a wall, or under certain join conditions.

Material Assets and the Visual API

A completely different aspect of materials is addressed by Gary J. Orr of MBI Companies Inc., who shares a nice VB.NET sample in the Revit API discussion forum thread demonstrating the interaction with Materials, Material Assets (Appearance, Structural, Thermal) and the Visual API. In his own words:

This has been a journey. The past few weeks have seen me spending all the time that I can spare reading the API help, reading posts going back years, reading blogs (which get really hard to pull targeted information out of sometimes).

Then came trying to put it all together, Snippets of code from the API, some of which can cause just as much confusion as the help that they provide, translating snippets of code that I can barely understand because I don't speak that particular coding language, which is made even worse by the fact that it is usually written by programming professionals that use code shortcuts (which makes reading their examples even harder) and often refer to advanced coding techniques that I simply haven't ever learned since none of this is my real occupation...

But hey, I did it.

I can now take materials and their related Assets apart and I can create/recreate them.

You are welcome to the attached VB file MaterialTools.vb if you are struggling with these same issues (dump it into a VS project with the appropriate references or copy the subs/functions into a VB macro with the appropriate mods for launching the included tools).

It has functions for collecting and logging the Properties of the Application resident Appearance Assets; the Assets within a Document; the materials within a document and their associated Properties and connected assets; and one for creating a new material with all new assets. Those functions will generate TXT and XML log files that can be used to further examine all of the above.

I have attempted to document (with in line comments) the steps required and/or taken, along with why one method may have been chosen over another when such applied, as well as some cautions and some still outstanding questions and/or shortcomings that had to be worked around.

If you're well versed in Materials, and have both the time and desire, perhaps you can take a look and see if you have any answers for the commented questions that I still have and have left in the code.

There are still plenty of issues, some may just be because I haven't learned all I need to learn yet, but the bigger ones seem to be common from what I have found: One biggie (in my opinion) is the lack of any direct correlation between the return values when you read Appearance Asset Properties to the Visual API Classes and their properties that should be used to work with them. This is a big complication and makes it really hard to avoid using strings and thereby keep your code language neutral. The descriptions in the API help do not help with that mapping the least little bit. Another would be the absence of any clear and concise method to differentiate between Physical Property Sets... Structural vs Thermal.

Of course, this may all just be an exercise in vanity from a wanna-be but perhaps some may find some of it helpful.

Later: Oops, The Asset Property type "List" was supposed to recurse to get the values of the list items, but I had forgotten to get back and actually make it do so.

That is fixed now. Along the way I decided to actually do something with that massive volume of returns from GetRevitAppearanceAssets, i.e.: application.GetAssets(AssetType.Appearance). So, I created an option to import one of each schema type into the current document.

I still don't know the actual source of these assets since they don't match any of the Revit libraries (and I still don't understand why we can't access any material libraries via the API), but I did find something else that is unusual: the only PrismWoodSchema that is contained in that library,

Materials and Visual API

shows as being an "Old Style" rendering Asset once it has been imported into the document:

Materials and Visual API

Yet, Assets using the PrismOpaqueSchema show as "New Style" Asset types (as would be expected for the above):

Materials and Visual API
Materials and Visual API

Just another glitch?

Here are the main problems I struggled with:

One biggie (in my opinion) is the lack of any direct correlation between the return values when you read Appearance Asset Properties to the Visual API Classes and their properties that should be used to work with them. This is a big complication and makes it really hard to avoid using strings and thereby keep your code language neutral. The descriptions in the API help do not help with that mapping the least little bit.

Another big one would be the absence of any clear and concise method to differentiate between Physical Property Sets... Structural vs Thermal.

We need a firm method of being able to differentiate Structural and Thermal Property Sets. PSE elements will often return both a thermal and a structural Property Set, and EVERY PSE element will return a structural (RevitLookup Snoop shows that pretty well)... yet they can only be used for the correct Property type, so something somewhere knows which one is which; we need that information in the API as well.

Then, we have: where does application.GetAssets draw from? There is nothing that matches what is found in that return list of assets anywhere that I can find in the system. The PrismWoodSchema returned from that list shows as an "Old style" rendering Asset once pulled into a document, even though it is clearly a procedural Asset.

We need access to the AEC material Library as well as any user created libraries (as can be browsed to in the Material Browser.

We need access to the Asset libraries (as can be browsed to in the Assets dialog).

There are a few other requirements in my comments within the code, but most end up boiling down to one of these.

Will that help get the development team started?

BTW: this was written to the 2023 API, I haven't tested it for compatibility beyond that.

Many thanks to Gary for sharing the results of his research!

Filter for Annotation Families

A quick note on filtering for annotation families:

Question: Is there an identifier which I can use to sort out annotation families vs other types of families using the Revit API? I'd like to iterate over the IEnumerable to then group items as I need them. The code I'm currently using and would like to modify:

  var families = new FilteredElementCollector(doc)
    .OfClass(typeof(ElementType))
    .Cast<ElementType>();

Answer: See if this works for you: https://forum.dynamobim.com/t/how-can-i-collect-all-family-types-that-are-considered-annotation-symbols-in-revit/37480/10

It shows you that you need FamilySymbol instead of ElementType.

Then, filter for:

  fs.Family.FamilyCategory.CategoryType
    == CategoryType.Annotation

Response: For some added context, I am hoping to first get all system families, then iterate over the collection of all system families and separate out the annotation families from that list. Ultimately, I will end up with two CSV's, one with Annotation Families only, and the other CSV file with all system families minus annotation families which is why I think I was using ElementType instead of FamilySymbol.

Answer: FamilySymbol should be fine in your case – it is derived from ElementType. ElementTypes are more general. For example, a Wall element can have a WallType which is also derived from ElementType.

Response: I gave it a try, but now system families I was hoping to extract (prior to filtering out annotation families), like a basic wall, do not come through in my filter.

Answer: Sorry my bad. You are right. For element types that are not family symbols you will not be able to do

  elem.Family.FamilyCategory.CategoryType

You may check its own category I guess.

Response: That got me on the right track. I ended up having to filter out items which have null category information:

  Category.CategoryType != CategoryType.Annotation

Thank you very much for your help!

Revit Batch Processor RBP

Let's wrap up with a pointer to the Revit Batch Processor RBP. It looks like a very powerful and full-fledged utility; it came up in the StackOverflow question asking is there a way to change the workset configuration of a Revit file without opening Revit?

The Building Coder Blog Post #2000

Actually, before closing, we have something to celebrate, a special issue, so to speak.

This blog post is The Building Coder's 2000th.

You can check out the entire chronological list in the GitHub tbc repository.

All in all, The Building Coder has published over four hundred thousand lines of text, two-and-a-half million words and over 22 million characters since its inception in 2008.

It is also interesting to see what topics we started out covering back then; several are equally relevant and still recurring today:

% wc *htm *md
      12     301    1909 0001_welcome.htm
       9     213    1366 0002_devtech.htm
      69     553    4297 0003_getting_started.htm
      68     756    5505 0004_revit_sdk_contents.htm
      19     497    3322 0005_managing_sdk_samples.htm
     122     916    6098 0006_sdk_samples_solution.htm
     120     865    5890 0007_loading_sdk_samples.htm
     125    1409    9349 0008_debugging.htm
       8     376    2121 0009_what_next.htm
     102    1062    8226 0010_selecting_all_walls.htm
     447    3131   32490 0011_units.htm
      66    1316    8954 0012_geometry_library.htm
      44     524    3712 0013_geometry_viewers.htm
...
     491    2762   20716 1997_sdk_brep.md
     267    1609   12882 1998_unittest_arcdim.md
     323    2249   18196 1999_mep_calculation.md
     259    2335   16834 2000_visual_api.md
  417722 2480900 22669323 total

Long live The Building Coder!

LinkedIn feedback from Mikhail Smirnov:

Thanks a lot for the article, Jeremy! As always, you are at the peak of the research wave.

Thank you for your appreciation! However, just to clarify: there is actually no research of mine in here at all. I am just as reporter, a librarian... there seems to be a greater lack of those than of researchers, programmers, and especially documenters... Researching, programming and documenting is much more fun! Still, someone has to do what I do, it seems...