Ryan Lenihan

Understanding the British Standard Object Naming Convention and Other Alternatives

Unless you’ve been living under a rock for the last few years, you’d be aware that almost globally the British Standards are the benchmark for all things BIM.

The international standard ISO19650 is based on the British BS and PAS1192 documents. Most naming conventions out there are based on BS 8451-1:2012. BS8451-1 covers naming and classification of BIM objects for architectural and engineering and construction. Natspec Open BIM Object Standard (OBOS), ANZRS? Yep you guessed it. They’re based on or derived from BS 8451.

Naming Options

There are two options in the British Standard. Objects that contain classification parameters and objects that don’t. What does this mean for you though? In it’s most basic sense if you drop the required classification information into parameters within the objects themselves, then you can have far simpler object names.

Don’t want to populate classification parameters? Well you’re naming content the long way.

Objects that contain classification attributes

If your object contains classification information within parameters, you don’t need to repeat that information in the family name. Providing data just because technology allows you to isn’t the right way to approach BIM.

BS8451-1 calls for “at least one classification to a UK convention” or “a classification from an equivalent generic classification text” so if you’re using Uniclass, Omniclass, Master Spec, it doesn’t matter as long as it is what your client has requested or you project team has agreed to.

The source or author is the library author, such as a company or the manufacturer themselves. This could be an abbreviated code, or it could be the full name.

It’s also not both the author and the manufacturer, it should be one or the other. It is who created the content. If the manufacturer had their content created by a third party, this is when it should the the manufacturer’s name or abbreviation. And if you’re not sure how to abbreviate your company name, if they’re listed on the stock exchange, that’s a good place to start!

The type is the first level of specialisation. A lot of people in the Revit world make the mistake of this being the Revit family category, which if we come back to the concept of “don’t repeat information just because you can” if you use the category as the type portion of the name, you’d missed an opportunity.

The subtype or product is optional but in my opinion it’s quite useful. It’s used to convey information not captured in attribute data.

An example of an object named in this manner could be my floorwaste gully family. In this instance the name might be:

RAU_Floorwaste_Iplex

Or say in the instance of a surface mount troffer lighting fixture, you might name it:

Acme_LightFixture_SurfaceMountTroffer

or better yet because our objects are already categorised, we can skip “LightFixture” from the name and run with something like this:

Acme_Troffer_SurfaceMount

In each instance, the remainder of the classification information and any other required information is contained within the object itself.

Objects that do not contain classification attributes

So this is where the naming can get tricky, and oddly enough much of the content I’ve seen over the years uses this long form of naming or some hybrid of this system to name their content, even if the object contain relevant classification parameters.

There isn’t a problem with this, if you want to use the long form naming convention that’s great, but just remember you want to keep your content and it’s naming simple and easy so your team is more likely to use it.

For these kinds of objects, the standard adds extra information to the front end of the object name.

The role is the role of the object owner on the project based upon the BS1192:2007 requirements for defined roles and responsibilities, for example LB for library provider and MN for manufacturer.

The classification is the coding from the classification system that your client has requested or the project has agreed to. Using my previous content examples

Floor waste:

Classification SystemClassification Code
UniclassPr_65_52_25
Omniclass23-31 27 00
Masterspec7151

Lighting fixture:

Classification SystemClassification Code
Uniclass Pr_70_70_48_85
Omniclass 23-35 47 11 11 11
Masterspec7741

BS8541-1 also permits the text description of the classification code to be included and separated by a colon, for example: Pr_70_70_48_85:Surface luminaires

Presentation is optional and describes the level of detail that the object is presented in.

The remainder of the fields remain the same, covering the Source or Author, Type and Subtype or Product.

Using the same families again, using this long form approach the families would be named as follows:

LB_Pr_65_52_25_LOD300_RAU_Floorwaste_Iplex

and for the lighting fixture

MN_ Pr_70_70_48_85_LOD200_Acme_Troffer_SurfaceMount

The ANZRS Method

The ANZRS method to object naming is

It’s arguably more agreeable naming convention as the first portion of the name has no risk of becoming an endless list of redundant company abbreviations that make it impossible to search for object alphabetically.

The FunctionalType portion of the name is the broadest descriptor that can be used. In the example of our floorwaste, that would be FloorWaste. It’ is made very clear in the ANZRS documentation that the functional type or the type have nothing to do with the Revit family category.

The Subtype contains the next logical level of information to descibe the element. In the example of my floor waste, I would want to know what kind of material it is made from, so I would use PVC.

This might vary greatly depending on the type of object and the kind of information you wish to convey.

The Manufacturer is obvious, if the object is intended to be generic, then simply enter Generic.

Descriptor is entirely optional, but it’s where you can provide additional information to help describe the object.

As with other naming conventions there are no allowances for spaces or hyphens. Underscores should be used to separate descriptors and multiple word descriptors should use PascalCase.

Using our same objects as examples again, using ANZRS they would be named as follows:

Floorwaste_PVC_Iplex and

LightFixture_Troffer_Acme_SurfaceMounted or more in line with the ANZRS descriptors would be:

Troffer_SurfaceMount_Acme_Flourescent65w

Natspec OBOS

The Natspec Open BIM Object Standard flips the BS8541-1 naming on it’s head in the same way ANZRS does, but OBOS gets a little more descriptive.

The Type, Subtype and Source are the same as the ANZRS functional type, type and manufacturer.

OBOS then continues to add further descriptors, starting with Product/Range Identifier which is used to identify the manufacturer’s product range or product identifier. This could be a part number.

The Differentiator is to provide additional information as required to describe the object or it’s material.

Finally, the Originator is a 3-6 character code to indicate who has authored the object.

Again taking our same families, they would now be named

Floorwaste_PTrap_Iplex_PVC_RAU and

LightFixture_Troffer_Acme_Flourescent65w_SurfaceMounted_ACME

Which is right for me?

As I’ll always argue, you need to make it easy for the team working with the content. In my opinion don’t overload the naming convention with coded abbreviations, especially if you’re using teams overseas.

My main two arguments when naming content will always be:

  • Don’t prefix your content with your company name or the author name (sorry BS8541, you’re doing it wrong). Not every company has a content management system to make searching easier. If you’re using Revit or file explorer to search for content, drop the author/company descriptor to the back of the filename like they do in ANZRS or OBOS.
  • Don’t waste your type or subtype descriptors by using the Revit family category. Use something that actually adds value to your content naming rather than something redundant. Again, it needs to be something that is easy to find without a content management system. Revit already sorts content by family category, adding that family category to the name of the object is a waste.

Another consideration to working with teams overseas and content naming, make sure that you understand some of the nuances in their use of English. For example, the Philippines is Americanised so many of the terms used in Australia or Europe may not be directly understood without explanation.

At the end of the day, it’s whatever works for you and your team. Make sure the naming convention is discussed with all stakeholders, that everyone can come to a compromise for the greater good.

Finally, make sure that it is all adequately documented so that anyone can jump in and understand what is going on.

View References. Use them!

So even though in the BIM world we produce models, guaranteed every project that you’re currently working on you’re also contractually obliged to produce drawings.

Drawings require things like notes and symbols, legends and linestyles.. and sheet or view references.

When you have a large architectural background or piece of linear infrastructure, you need to split your plans up over multiple sheets. When this happens, it’s best practice to include a sheet reference so whoever is reading the drawing knows where to go to keep on reading.

Usually this is common sense, it’s the next sheet in the drawing set. Even when this is the case though, you still need to provide a sheet reference.

With all the data you have at your fingertips in the model, for some reason people still insist on using text notes. That’s dumb text for those playing at home.

So let’s paint a picture. You’re working on a piece of linear infrastructure, and there is now a requirement for an additional 3 or 4 drawings at the front end of the set. You have text notes to update, across 58 drawings!

Why are you torturing yourself (and me)?

Use view references. They’re a handy dandy little family type that you can reference the view itself, which then provides the sheet number that the view resides on. If the drawing numbers change, then everything is updated automatically. No need to edit dumb text notes.

It might seem like a chore when you first set it all up and link the references to the correct views, but then for the rest of the project it’s smooth sailing. Why make life harder for yourself in the long run?

Don’t get mad. Get view referencing.

After fixing up an entire series of drawings and sending a few astounded and frustrated messages to some mates over WhatsApp (sorry guys!), I got to the end of my list of views and realised there was an out of sequence view.

Ignoring that there is clearly an extra step in the scope box naming vs. the view naming, the very last view in the list “Sheet 37” had scope box 23 applied to it.

Well.. no need to get mad, after renaming the incorrect views in Excel and slipping the new view 24 into the sequence, I only had to fix up the view references on views 23 and 25 to reference the new 24 and create the new references on view 24.

Could you just imagine if you had to fix that up if it was a dumb text note? Especially after you had just run through the entire sequence of views.

I’d be channelling my inner Moss.

Revit 2020.1 Prompts to uninstall when you want to install

This is one I have unfortunately come across too many times, you notice that you can’t access BIM360 in Revit 2020, you check your version and you have plain old 2020 which is before the critical TLS patch was released. Easy, I’ll install the 2020.1 patch.

But Revit says no. Update to 2020.2 right? Well.. no. The project you’re working on for the short term is locked into 2020.1 to keep versions consistent. So what are we going to do?

Well, thankfully there are a lot of helpful people out there on the big wide world of the internet, for this one Tony Michniewicz posted up the solution on the Autodesk Support Forums

Open REGEDIT, you will need admin rights for this one. Even local admin rights will do.

The browse to the following registry key

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\

Once there, right click on the key and select find from the contextual menu and search for Autodesk Revit 2020.1.

When you find the existing registry key, simply delete it and then re-run the 2020.1 installer.

Nested Annotation Symbols 101

Over the years, I’ve seen some interesting Revit content. One of the doozies though always seems to be the use of nested annotations and for whatever reason, these weird and wonderful annotation solutions always seem to be in electrical families.

The idea of nesting an annotation symbol in a 3D family is so you can present both a “real life” display for coordination purposes and a symbolic representation for your drawings.

There is no need to get creative with visibility controls

At their most basic level, nested annotations should be controlled by the coarse/medium fine visibility settings.

Simply select the nested annotation in the 3D family, click the visibility/graphics ovverides button in the properties dialogue and then choose when you want the symbol to appear. Generally at fine mode the symbol would be switched off, coarse and medium you should follow your company standards. As the detail level of the view changes, the visibility of the symbol follows suit.

I’m not here to stifle creativity, far from it. But if you’re setting up families and you feel like you’re having to come up with creative solutions for simple things like the display of symbols, chances are you’re doing it wrong.

Yes/No check boxes instead of detail level. Just don’t do it.

In the above example, the family has an instance based Yes/No visibility parameter. This is painful for not only the the end user to work with, but could you imagine giving a model configured this way to an architect and then explaining to them if they need to coordinate on ceiling equipment they need to turn off a series of instance parameters to do so? Yeah nah.

Maybe the more frustrating thing about this particular example is that the 3D components of the family are not controlled at various detail levels either. If you have the symbol on, both the 3D and 2D content is visible all the time which in turn results in your graphical display on drawing to get a bit weird.

Take this example below, how will a contractor on site be able to tell what this even is once printed as a hard copy?

Unfortunately there is no way to be nice or beat around the bush about this one. Just use the provided built in detail level controls in your families. It’s not even difficult to use!

That old annotation orientation chestnut

As a hydraulic guy, I’m rather jealous of the maintain annotation orientation option that electrical and comms families have so I’m sure you understand when I get a little upset with some content creator’s solution to symbology display in families that need to be oriented differently depending on the particular scenario.

The brilliance of this option is that regardless of the rotation of your family, the symbol will display correctly. Need a power outlet on a wall, in a floor box or on the ceiling? No need for multiple families or content creation trickery! Just tick the box!

Upside down, back to front, sideways, no matter what you’ve got the annotation will always appear correctly!

Movable annotation symbols

I’ve talked about movable annotation symbols a long long time ago, and let’s face it, they’re pretty great. But they have a time and place; usually for power and data outlets and the like.

Where they should never be used are things where the symbol is normally shown at actual object size, and is in fact a detail item, not an annotation symbol. Objects such as pits, lights, air terminals.. that’s all a big no for movable annotation symbols.

Why though? Well sit down, pour yourself a drink, grab some popcorn and let me tell you about a story of terrible coordination..

Flip it round

The last common problem that I have seen with content is the use of flipped and mirrored family symbol variations. This particular example is my favourite as well, you see the symbol is supposed to flip by turning on/off the alternate symbol.. but it looks like someone forgot to do that.

Forgetfulness aside, there is a method that can be used particularly for these 3D families that have consistent geometry built of a revolve.

Adding a flip in the direction it’s required will flip both the annotation symbol and the geometry, so just be careful when you use it. In the above example of the smoke detector, the flip control is no drama but in the instance of something such as a power outlet on a wall, using the flip will flip the geometry into the wall itself.

I would however ask the question, is the flip actually required on the symbol? What value does the flippable symbol add to your template? Avoid adding unnecessary complexity or configuration wherever possible in content, especially where standardisation is key. If there are options available on symbol display, then it’s not standard.

Up, Down, Up, Down.. and left and right.

I often see this in families that contain text in the symbol. Some variation of instance based yes/no visibility check boxes to show a correctly oriented symbol and text.

The example above actually relies on a combination of the left and front visibility check boxes to in turn display the correct symbol. The other more obvious variation of this solution that I’ve seen is the literal up, down, left, right parameters. Either way, it’s what we in the industry call a shemozzle.

As with most of these other creative solutions, there is actually an out of the box solution to handle the orientation of text within symbols.

The keep text readable option does just that, it keeps the text readable in line with standard drafting practices of the text being readable from the bottom or the right hand side of the page.

As you can see in the example, I have created a simple arrow family to clearly display the direction the family itself has been rotated. The symbol itself is a nested annotation with text and linework for the box.

With the annotation family correctly configured with the keep text readable tag, the text always appears correctly without introducing the possibility of human error.

Do you have any other tips on working with nested annotations in Revit families? If so post them in the comments section below

The Drawing Status

The drawing status is maybe overlooked as something simple, but there are many different ways to approach it, some being more cumbersome to mange than others.

Checkboxes

First we have the instance based checkbox. It’s a great method if you have something like Ideate BIMLink available to you, or a well developed Dynamo graph, but if you don’t have either of these then the manual checking of boxes across a project becomes tedious quite quickly.

Even with these tools to speed up your work, setting the status of these titleblocks can become quite the chore if you have a lot of statuses to work with like the above example and you have differing statuses from sheet to sheet.

rows and rows and columns and columns and rows and rows..

Even though you can copy cells in Excel, I wouldn’t want to be the person given the task of ensuring the accuracy of these status stamps.

So how do we improve on this?

Integers

The next approach to managing instance based on/off visibility, the integer. I’ve discussed the use of integers to control visibility in the past and it certainly makes management of visibility much easier.

As you can see in the above example, we have a single integer value controlling the visibility of all the status options. As you can see, we have a series from 1-10 and another series from 21-28, this is differentiate between statuses in different geographical regions. But we can do better than that!

Although it’s slightly more complex and gives an additional integer to define, if you have consistent statuses between regions that need to be displayed differently, then you have the benefit of both regions having the same status code, such as 4 for tender issue.

Sure you need to either have a list in front of you or at least remember what number corresponds to which status, but both of these solutions are much easier to manage in an Excel based workflow as you simply need to export one or two parameters in addition to the sheet number.

Another simple alternative to controlling the region setting is to have the integer as a type based parameter and create a titleblock type per region or office, that way there is no need for the end user to set the integer controlling the region within the titleblock at all.

Checkboxes and Family Types

The family type parameter method is another way to approach this simple task, however it makes things a little trickier as the parameters aren’t easily manipulated with BIMLink.

Often I’ve seen titleblocks using this method to still use yes/no visibility check boxes within the nested family. The nested family types can then be controlled using an instance parameter in the titleblock, which provided that you name your nested family types in a simple easy to understand manner, it gives the end user an easy to understand way to control the status on a per sheet basis.

The Free-for-all Instance Label

An option that is rarely used and probably for good reason is the free-for-all label. This is where the status is controlled by an instance text parameter tied to a label in the nested annotation family. The instance parameter is then linked through another instance parameter to the status family.

As status stamps are something that are standardised and shared parameter labels by their nature are a free for all text box, it’s a good reason to not use a label. However, if you have a good BIM or drafting lead overseeing the project and ensuring the correct standards are being applied, then there is no reason why using labels should be out of the question. There will always be that risk of inconsistency though.

Labels and Family Types

Finally, probably the best option is the nested annotation symbol with a label that is controlled by a type parameter. This method is both easily controllable in an Excel or Dynamo based workflow and is a clean and lightweight option.

Regardless of the method you choose, make sure it’s easy for the end users to adopt and if there is anything at all that needs to be documented, make sure it’s documented clearly and concisely.

From Dynamo to C# – Get and Set Parameters

I’m the kind of guy that uses Dynamo for practical things, an indispensable swiss army knife that I used to manipulate data.

Getting and setting parameters is obviously key. This time around we look at how this is done in Dynamo and then how we can achieve that same thing with code.

In this example I will look at how to build up an asset code based on the level name and the mark parameter, combining those into a single new parameter value.

We will start with the code from the previous Dynamo to C# post where we collected all the AHU families in the model. For convenience, here is the complete code from the previous post:

public void getMechanicalEquipment()
{
	//get current document
	Document doc = this.ActiveUIDocument.Document;
	
	//setup collector and filter
	var collector = new FilteredElementCollector(doc);
	var filter = new ElementCategoryFilter(BuiltInCategory.OST_MechanicalEquipment);

	//collect all mechanical equipment
	var allMechanicalEquipment = collector.WherePasses(filter).WhereElementIsNotElementType().Cast<FamilyInstance>();

	//use LINQ to filter for familyInstances containing AHU in the family name
	var ahuEquipment = from fi in allMechanicalEquipment
		where fi.Symbol.FamilyName.ToUpper().Contains("AHU")
		select fi;

	//setup string builder to store data
	StringBuilder displayInfo = new StringBuilder();

	//loop through, collect data
	foreach (FamilyInstance fi in ahuEquipment)
		{
			var elementinfo = string.Format("Family Name: {0} Element Id: {1}", fi.Name, fi.Id);
			displayInfo.AppendLine(elementinfo);
		}

	//display results
	TaskDialog.Show("Watch", displayInfo.ToString());
}

Getting the Level

To do this in Dynamo, you would need to string together the following nodes:

In Dynamo we need to use the Level.Name node to get the actual name. The reason for this is that when we feed Level into the Element.GetParameterByName node, the output is actually an element id. Feeding that element id into the Level.Name node returns the name without any further work required.

So how do we replicate this in C#?

First, we need to focus on the foreach loop, as this is where the work is done for each element that we have selected. We have already done the groundwork to select our elements, but how do we find out what level each element is associated with?

We need to use the level class from the API, this allows us to retrieve the name of the level as you would see it when inspecting the element you’re working with.

Using the level class, we create a variable theLevel which we find from the family instance fi.

Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;

This line is doing half the work of our Level.Name Dynamo node. It is taking the element id of the level that is reported from the Level parameter of the selected AHU family, then selecting the level element.

From there we can simply use theLevel.Name to display the level name.

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
{
	Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;
	var elementinfo = string.Format("Family Name: {0} Level: {1}", fi.Name, theLevel.Name);
	displayInfo.AppendLine(elementinfo);
}

Note that instead of using the elementinfo variable to display the family name element id of the AHU in the dialogue box as per the previous example, we are now displaying the family name and the associated level name.

Getting the Mark

There isn’t a whole lot of change between the process for picking up the mark parameter in Dynamo compared to the level name; the only real change is that we can drop the Level.Name node.

This is because when we tell the Element.GetParameterValueByName node that we want a parameter named Mark, it returns the result as a string.

In C# we want to use get_Parameter on the family instance fi.

But before we get too far, we need to understand that built in parameters are handled slightly differently in Revit to user defined parameters. Built in parameters are contained within an enumerated list, you can review the list of built in parameters on Revit API Docs.

In the API, the mark parameter is stored as ALL_MODEL_MARK

Retrieving built in parameters this way is more reliable as there is no risk that you will pick up a user defined parameter of the same name.

Now that we know this, our equivalent line of code to read the mark parameter in C# looks like this:

Parameter theMark = fi.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);

But we still don’t have the parameter value. To retrieve the parameter value, we need to use the AsString method on our paramter theMark which will return the parameter value as a string.

It is important to use the correct method to avoid any potential errors returning the data, for numbers use AsDouble, for integers use AsInteger and element ids use AsElementId.

Our foreach loop should now look like so:

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
{
	//get the level
	Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;
	//get the mark
	Parameter theMark = fi.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);

	var elementinfo = string.Format("Family Name: {0} Level: {1}", fi.Name, theLevel.Name);
	displayInfo.AppendLine(elementinfo);
}

Again, adding this to our task dialogue, we end up with the following results.

Building the New String

We can build our new parameter value a few different ways in Dynamo, either by using the String.Concat node, or by using a code block which is my preferred method.

There are also a few different way you can approach building the new string in C# as well. The simplest way is to approach it the same way as we do in the Dynamo code block.

First you define the string, in this example we are naming it newValue and we take our level name theLevel.Name, add the string _AHU_ and the we take our mark value with theMark.AsString().

string newValue = theLevel.Name + "_AHU_" + theMark.AsString();

The other method is to use a string builder which we have been using in our examples to create our dialogue box message. The string builder version would look like so:

var newValue = string.Format("{0}_AHU_{1}", theLevel.Name, theMark.AsString());

Either method is acceptable, but if you’re building more complex strings the string builder is the recommended way to go. Both options give the exact same results:

Setting the New Parameter

In Dynamo, the job here is done by the trusty Element.SetParameterByName node seen above, but in C# there is a tiny bit more to do.

The first thing we need to do to set out parameter is define which parameter we want to set. In this example I’m going to use the built in parameter Comments which is stored in Revit as
ALL_MODEL_INSTANCE_COMMENTS.

Parameter theComments = fi.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);

The next step is to start a transaction. Every time you want to change something in a Revit file via the API, you need to make the changes inside a transaction. If you have ever looked into Dynamo nodes containing Python code, you would have seen something similar to this before.

//set the parameter
using(Transaction t = new Transaction(doc, "Set Parameter"))
{
	t.Start();
	//do stuff here
	t.Commit();
}

We define a new transaction t which is being performed in the current document doc which will appear in the undo/redo list as Set Parameter.

Inside our transaction, we set the parameter by using the Set method on our parameter theComments.

//set the parameter
using(Transaction t = new Transaction(doc, "Set Parameter"))
{
	t.Start();
	
	theComments.Set(newValue);
	
	t.Commit();
}

And that’s it! We have successfully pulled information from multiple parameters, generated new string data and set another parameter using C#.

The complete code is below:

public void getsetParameters()
{
//get current document
Document doc = this.ActiveUIDocument.Document;

//setup collector and filter
var collector = new FilteredElementCollector(doc);
var filter = new ElementCategoryFilter(BuiltInCategory.OST_MechanicalEquipment);

//collect all mechanical equipment
var allMechanicalEquipment = collector.WherePasses(filter).WhereElementIsNotElementType().Cast<FamilyInstance>();

//use LINQ to filter for familyInstances containing AHU in the family name
var ahuEquipment = from fi in allMechanicalEquipment
	where fi.Symbol.FamilyName.ToUpper().Contains("AHU")
	select fi;

//setup string builder to store data
StringBuilder displayInfo = new StringBuilder();

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
	{

		//get the level
		Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;

		//get the mark
		Parameter theMark = fi.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);

		//get the comments
		Parameter theComments = fi.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);

		var newValue = string.Format("{0}_AHU_{1}", theLevel.Name, theMark.AsString());

		var elementinfo = string.Format(newValue);
		displayInfo.AppendLine(elementinfo);

		//set the parameter
		using(Transaction t = new Transaction(doc, "Set Parameter"))
		{
			t.Start();

			theComments.Set(newValue.ToString());

			t.Commit();
		}

	}

}

Revit Bulk Unit Conversion Addin

One of the most downloaded files here at Revit.AU is the metric conversion journal script. Despite the obvious downsides of journal scripting, it works about 98.27% of the time.

Based on feedback, the problems with the current journal conversion script are mostly due to user error, but occasionally even when the user followed the steps correctly, a problem would crop up that would stop the process in it’s tracks.

Because of this, one of my goals when learning how to use the Revit API has been to replace this journal script with a fully fledged addin. In my opinion, the biggest benefits seen by developing an addin to perform unit conversions are:

  • User-friendly interface
  • Ability to convert to both metric and imperial in the same tool
  • Automatically closes most popup dialogues
  • Handles sub-folders of files without issue
  • Much, much faster

Benchmarking

If I’m honest, I regret my choice of benchmarking method, a much smaller dataset would have done the job.

I rounded up 500 families totaling 169mb and ranging in size from 248kb to 1.5mb comprising of both 2D annotation and detail items as well as 3D components from architecture and MEP disciplines. I ran the same set of families through both the journal file conversion method and the addin conversion method, recording the total elapsed time for each.

I also used two different hardware configurations were in the benchmark tests; an i5 8400H with 32gb RAM and a 512gb NVME SSD and a i7 7820HK with 64gb RAM and a 1tb NVME SSD. The tests were run on Revit 2018.3 on both machines.

I hit the go button and decided to get some breakfast. This is going to take a while.

2 hours and 3 minutes later on the i5 8400H, the journal script failed on family 366. Repeating the same test on the i7 7820HK reached the failure point in around 1 hour and 40 minutes.

The addin, on the other hand, churned through all 500 families without error in 14 minutes on the i5 and 12 minutes on the i7.

Call it commitment or call it stupidity, but I also decided to time how long it would take to change the content in a single family manually to really showcase the power of automation, even if it’s on a tiny scale.

Manually updating a single family to change everything that the addin changes took 7 minutes and 18 seconds.

Of course, you normally wouldn’t change everything that the addin does, but we all love an apples to apples comparison. If you’re feeling like you need some extra excitement in your life, based on 7 minutes and 18 seconds to change a single family, manually updating 500 families will keep you busy for almost 61 hours!

Reliability

So far, I have processed close to 5000 different families from a range of different sources across all versions of Revit from 2017 to 2020 without a single error.

And let’s face it, although 365 of 500 families were processed with the journal, I couldn’t complete my automation without an error.

Couple the reliability of the addin with its ease of use and as an end user, you should have a vastly improved experience compared to the journal script.

Download

So if converting files between metric and imperial is something that you do, you can download the addin for free here:

Think this tool is worth something? Consider donating to support the operation of the site and the development of further tools in the future.

From Dynamo to C# – All Elements of Category

I had a few comments in response to my post on using the API to clear out a problem line pattern, mostly that the API is over the average user’s head. While this may be the case, there are other users out there that have been using Dynamo for a while now and are looking to take their solutions to the next level.

So I thought it might be useful to show comparisons between what some basic tasks in Dynamo look like, and then what they look like in code. And as I myself am still learning, if you’re a more experienced coder, feel free to correct me if I’ve made any mistakes or if I have taken an around-about method that could be simplified.

It is worth noting that the code examples provided below are from macros, not loadable addins. There are slight differences between the requirements of macros and addins that you can read on Harry Mattison’s blog BoostYourBIM

The first example is getting all families of the mechanical equipment family category. In Dynamo, you would achieve this with the following nodes:

In C# there is a little bit of groundwork that is required first. You need to tell Revit to work with the active open document. To do this you use

//set the active document
Document doc = this.ActiveUIDocument.Document;

In this line, doc is a variable that you are setting and can use later throughout your code to call the active document.

The next step is to create a FilteredElementCollector to collect all the mechanical equipment families in the model.

//setup collector and filter
var collector = new FilteredElementCollector(doc);
var filter = new ElementCategoryFilter(BuiltInCategory.OST_MechanicalEquipment);
 
 
//collect all mechanical equipment
var allMechanicalEquipment = collector.WherePasses(filter).WhereElementIsNotElementType().Cast<FamilyInstance>();

We are filtering our families by the built in category OST_MechanicalEquipment. The variable fi represents every family in the model of the mechanical equipment category.

You can however filter any other family category, there is a list of built in categories in the Revit API documentation, the online version of this documentation is here.

But say you wanted to filter further? Selecting the mechanical equipment in the model is far too broad. What about filtering by the family type name? In Dynamo you would achieve this with the following nodes:

As you can see, I’m filtering the families to see if they contain the string “AHU”

In C# we need to take our allMechanicalEquipment variable and filter the list of mechanical equipment families by using a LINQ query to find elements that contain the word AHU.

//filter for ones containing AHU in the name
var ahuEquipment = from fi in allMechanicalEquipment
                   where fi.Symbol.FamilyName.ToUpper().Contains("AHU")
                   select fi;

You can read more about LINQ queries on the Microsoft website:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/query-syntax-and-method-syntax-in-linq

You will also notice here that when we take the family name using fi.Symbol.FamilyName we have added ToUpper(). The reason for this is that some families may have been named inconsistently such as Ahu or ahu. Converting the family name to uppercase means we catch everything with AHU in the name regardless of text case.

So now that we have all of our AHU families, what about replicating that watch node? In Revit we can use the TaskDialog to display information back to the user.

When we want to do something with each of the AHU families, such as getting their name and element id, we need to use a foreach loop which repeats the same process to each selected element.

//example foreach loop
foreach (FamilyInstance fi in ahuEquipment)
{
//do stuff here
}

So we are going to collect the names and element ids for each family instance fi in our list ahuEquipment

First we need to do a little groundwork by defining an empty stringbuilder to populate with information. We do this with the following code:

//setup string builder to store data
StringBuilder displayInfo = new StringBuilder();

Then inside the foreach loop, we need to collect the information on our families and add them to a variable (var) named elementinfo.

We build each line in our string with fi.Name which takes the name of our family instance as well as fi.Id which is the element id of our family.

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
{
    var elementinfo = string.Format("Family Name: {0} Element Id: {1}", fi.Name, fi.Id);
    displayInfo.AppendLine(elementinfo);
}

To display this information, we need to add the following line outside the foreach loop.

TaskDialog.Show("Watch",displayInfo);

The result is the following dialogue box displayed in Revit

Don’t forget to add spaces around your strings like I did

So to wrap everything up, our final code dropped into our macro looks like so:

//get current document
Document doc = this.ActiveUIDocument.Document;
 
//setup collector and filter
var collector = new FilteredElementCollector(doc);
var filter = new ElementCategoryFilter(BuiltInCategory.OST_MechanicalEquipment);
 
//collect all mechanical equipment
var allMechanicalEquipment = collector.WherePasses(filter).WhereElementIsNotElementType().Cast<FamilyInstance>();
 
//use LINQ to filter for familyInstances containing AHU in the family name
var ahuEquipment = from fi in allMechanicalEquipment
                   where fi.Symbol.FamilyName.ToUpper().Contains("AHU")
                   select fi;
 
//setup string builder to store data
StringBuilder displayInfo = new StringBuilder();
 
//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
{
    var elementinfo = string.Format("Family Name: {0} Element Id: {1}", fi.Name, fi.Id);
    displayInfo.AppendLine(elementinfo);
}
 
//display results
TaskDialog.Show("Watch", displayInfo.ToString());

And yes, I admit coding is a big jump from stringing together some Dynamo nodes, but just like learning Dynamo the more you dive into it the simpler it becomes.

Is DWF Better than PDF?

If you ask anyone in the building services industry what they use to markup drawings, I can almost guarantee that no one will answer with Design Review. The large majority will say Bluebeam, a few will say Acrobat and then the luddites will say pen and paper.

Unlike Bluebeam and Acrobat, Design Review doesn’t implement a printer on your system to generate the files it needs, it doesn’t review PDF files and you can’t use it as a dodgy photoshop as some seem to use it for. Design Review as the name implies is made to do one thing and one thing and it does that one thing rather well.

Many had thought that Design Review had been abandoned by Autodesk, with no releases since 2013. Then Design Review 2018 happened.

Check out the process either here on REVIT.AU or watch a demo just below on Youtube.

Exporting Sheets as DWF

To get started, we need to export a DWF from Revit. That’s right, DWF files are used for more than just CostX. In this example we will be using the trusty rac_basic_sample_project.rvt

We’re also using Revit 2019 in this example, if you’re using a previous version of Revit you need to click on the Application Menu (the big R) rather than the file menu.

Just like using the default printing or DWG interfaces, select the views and sheets that you want to export

There are additional options that can be set and applied to the DWF export it’s always great to have a bit of extra metadata within your files, but only fill out if required. There is no point in providing information just because the technology allows you to.

Save your DWF in a convenient location, ready to send off to your friendly local engineer

The Design Review Interface

On opening the file you’re presented with a series of thumbnails on the left hand side of the screen which represent each of the drawing sheets and views exported from the model.

There are also additional tabs that allow you to review sheets and views in a list mode, list your markups and also and exported model views.

Navigating between sheets is as simple as a single click on the sheet in the list. This isn’t Internet Explorer, no double clicking here baby-boomers! Those extra mouse clicks are just precious time that’s wasted!

The right-hand side of the screen provides you with handy tabulated information regarding the open sheet or view. These tabs can be pinned or set to auto-hide depending on your preference.

Markup and Measure Tools

Design Review includes all the makrup and measurement tools that you need to get the job done.

In addition to the usual suite of 2D markup and measurement tools, there are a series of handy tools that can be used in 3D views so you can take sections through the model assist in the markup process.

To further assist with the markup and review process, there is a sheet compare functionality which allows you to compare drawings between two DWF files.

But what about symbols? Most engineers I’ve spoken to say that they can’t give up Bluebeam because “it has symbols”. Acrobat also has symbols. Just saying.

Design Review doesn’t rely on the set of American symbols you found on the internet, or that Bob stole from his previous company for Bluebeam.

Design Review is able to import 2D symbols directly from a DWF file. What this means is that your specific company standard of block or family libraries can be exported from AutoCAD or Revit and imported directly into Design Review.

Remember the markup tab from earlier? As you generate markups, they are listed out within the markup tab and sorted by drawing sheet.

Each markup has additional information attached, reflecting any notes on the markup and the markup history.

Wait. What? The markup history? That’s right, Design Review offers a full round trip functionality.

Why we should all be using Design Review

Have you ever noticed that DWF Markup button in Revit and wondered what it is all about?

It’s only been there for almost forever.

It’s all about Design Review.

For the longest time, we have had the ability to link in DWF markups directly to Revit and it’s a good sign that Autodesk won’t abandon Design Review.

When linking in a DWF markup file, Revit will display the sheets that include markups. Any sheets without markups will not be loaded.

When the markups come in, they overlay on each sheet.

All the information from the DWF markup flows through into Revit, making it easier for markups to be actioned quicker and hopefully more accurately.

Markups can be completed in most instances by activating the current Revit view and making changes on the spot.

As work is progressed, modellers can change the status of the markup which will highlight it as complete.

There is also opportunity for the modeller to add their own notes to the markup for when it is returned to the engineer.

Once all the markups have been completed, the drawings can be exported as DWF again for the engineer to review.

And the Best Part?

Design Review is free

https://www.autodesk.com.au/products/design-review/overview

Build Your Own Unassisted PowerShell Uninstallers

A fair chunk of what I do these days in the office is around testing software prior to packaging and deployment.

If you have ever had to install and uninstall Autodesk software for testing purposes, or you just wanted to get rid of an old version of the software you’d know that it’s not as simple as it probably should be. Rather than just uninstall Revit, you need to uninstall Revit and a whole host of other applications.

Yep.. that’s a lot of clicking

A while ago, I posted a solution for how to uninstall the 2015 Building Design Suite with PowerShell, the problem is however that this solution no longer works with the current Powershell; it was written for v2.x and Windows 10 is deployed with v5.x

To get the job done in Windows 10, first we want to get a list of all the installed applications on the machine, I just want the name of each package so we need to type the following at the PowerShell prompt. Of course make sure that you’re running PowerShell as an administrator.

Get-WmiObject -Class Win32_Product -Computer . | select-object Name | Export-CSV -path c:\ListSoftwareResults.csv -notypeinformation

This produces a handy little *.csv file with a list of all the installed applications

You can actually pull more information than just the name, it’s as simple as separating the properties with a comma

Get-WmiObject -Class Win32_Product -Computer . | select-object IdentifyingNumber,Name,Vendor,Version,Caption,LocalPackage | Export-CSV -path c:\ListSoftwareResults.csv -notypeinformation

From here we need to wrap the names of our software into this handy little script.

# Remove applications if installed.
$programs = @(
"Software Name 1",
"Software Name 2"
)
foreach($program in $programs){
Write-Host "Looking for $program."
$app = Get-WmiObject -Class Win32_Product ` -filter "Name = '$program'"
if ($app -ne $Null) {
Write-Host "Uninstalling $program."
$app.Uninstall()
Write-Host "$program uninstalled."
}
else
{
Write-Host "$program not found."
}
}

So for example, if we just wanted to uninstall Revit 2019 and it’s associated packages, we would use the following

# Remove Revit 2019 applications if installed.
$programs = @("Autodesk Revit 2019.2",
"Autodesk Revit 2019.1",
"Autodesk BIM 360 Revit 2019 Add-in 64 bit",
"Autodesk Revit Infraworks Updater",
"FormIt Converter For Revit 2019",
"Revit 2019",
"Autodesk Revit 2019 MEP Fabrication Configuration - Metric",
"Autodesk Advanced Material Library Base Resolution Image Library 2019",
"Batch Print for Autodesk Revit 2019",
"Autodesk Collaboration for Revit 2019",
"Autodesk Material Library Medium Resolution Image Library 2019",
"Worksharing Monitor for Autodesk Revit 2019",
"Autodesk Revit 2019 MEP Fabrication Configuration - Imperial",
"Autodesk Material Library Low Resolution Image Library 2019",
"Autodesk Revit Model Review 2019",
"Autodesk Advanced Material Library Low Resolution Image Library 2019",
"Autodesk Workflows 2019",
"Autodesk Material Library Base Resolution Image Library 2019",
"eTransmit for Autodesk Revit 2019",
"Autodesk Material Library 2019",
"Revit IFC 2019",
"Autodesk Revit Content Libraries 2019",
"BIM Interoperability Tools for Revit 2019",
"Autodesk Advanced Material Library Medium Resolution Image Library 2019")
foreach($program in $programs){
Write-Host "Looking for $program."
$app = Get-WmiObject -Class Win32_Product ` -filter "Name = '$program'"
if ($app -ne $Null) {
Write-Host "Uninstalling $program."
$app.Uninstall()
Write-Host "$program uninstalled."
}
else
{
Write-Host "$program not found."
}
}

Before you run any of these scripts though, you will need to change your execution policy. You can do this just for the current PowerShell instance rather than permanently allow scripts to be run on the system. To do this, it is as simple as

Set-ExecutionPolicy unrestricted

To run the script you need to include the full location of the script, even if you are running it from the current folder. For example

.\Uninstall_Revit2019.ps1

If you are specifically dealing with Revit software, you can take your uninstall a step further and clean out the associated files along with it. To do this, just add the following to the end of your script.

# Remove Revit 2019 user data.
Write-Host "Cleaning Revit 2019 User Data"
Write-Host "Backing up old user profile"
Move-Item -Path "C:\Users\$env:UserName\AppData\Roaming\Autodesk\Revit\Autodesk Revit 2019" -Destination "C:\Users\$env:UserName\AppData\Roaming\Autodesk\Revit\Autodesk Revit 2019_OLD"
Write-Host "Deleting temp files"
Remove-Item -Path $env:temp -Force -Recurse
Write-Host "Deleting user profile temp files"
Remove-Item -Path "C:\Users\$env:UserName\AppData\Local\Temp" -Force -Recurse
Write-Host "Deleting user profile Revit cache files"
Remove-Item -LiteralPath "C:\Users\$env:UserName\AppData\Local\Autodesk\Revit\Autodesk Revit 2019\CollaborationCache" -Force -Recurse
Write-Host "Deleting local machine Revit cache files"
Remove-Item "C:\Users\$env:UserName\AppData\Local\Autodesk\Revit\Autodesk Revit 2019\CollaborationCache" -Force -Recurse
Write-Host "Deleting Revit journal files"
Remove-Item -Path "C:\Users\$env:UserName\AppData\Local\Autodesk\Revit\Autodesk Revit 2019\Journals" -Force -Recurse

Just make sure that if you remove the user data as part of your script that you need to run it from that user’s profile, not from your administrator profile.