It’s often these days that you have to reload Revit models almost on a weekly basis depending on the model exchange frequency on your project. Or you have upgraded your project or restructured your directories. We’ve all been there and most likely wondered why we can’t just reload all from a directory (or several directories)? It is a pain when you have a large amount of models to reload, you have to go through each one individually hitting ReloadFrom if the filename or location has changed. Meh!
Well, I say nay to that. I don’t know about you, but I hate repeating the same boring task over and over again. It irks me a great deal. There are some add-ins that can do this for you (although, I think this should be a built-in feature), we can do this ourselves and create a more custom ReloadFrom Automation routine that suits our project, company directory structure, model naming protocols etc…
So, I contrived this little script that allows me to easily ReloadFrom on a Revit Link or multiple Revit Links at once. You can give it multiple directories too meaning that you can be somewhat lazy and and not care too much if the .rvt file you want to load is definitely in there (although it does help :)). Also, just like the ReloadFrom found in the UI, you can reload a completely different model if you really want (useful for models with revision sequences at the end of the name…but, also quite risky if you get your logic wrong for obvious reasons).
Personally, I will use this for an Auto-Reload script that will find and Reload the latest Revit files within sub-folders in a given directory. Then I know that I always have the latest Architectural or MEP models loaded. And, whenever a new Model Exchange happens, I just run the script and I’m good as gold again.
The fun bit…
The nodes that do most of the heavy-lifting for Reloading the models are the LinkedDoc.Reload (Py) and the WorksetConfigOptions (Py) shown in the graph extract above. For the Get Linked Revit Models (Py) script, see the RevitLink Utils post (ToDo).
Here is the code for each one…
WorksetConfigOptions (Py)
import clr clr.AddReference("RevitAPI") from Autodesk.Revit.DB import * import System from System.Collections.Generic import * # return enum values... OUT = [i for i in System.Enum.GetValues(WorksetConfigurationOption)]
This is using the System.Enum.GetValues (more on Enums here) to return all the values in the WorksetConfigurationOption Enumeration. The reason why I added this is to have an option to be able to choose how we load the worksets in the Revit Document we are about to link in (similar to when you link a Revit Model you can use the drop down arrow next to the open button) although this is not absolutely necessary. If we look at the LoadFrom() method in the RevitLinkType Class you can see that there are two overloads and each requires two arguments. I am using the one that takes the arguments…
RevitLinkType.LoadFrom(ModelPath, WorksetConfiguration)
And the WorksetConfigurationOption Enum value is something that we can set when constructing a WorksetConfiguration(WorksetConfigurationOption) object to pass into the LoadFrom class shown above.
Anyway, having done that easy node, we can now get onto the larger and more complicated node that will do nearly all the hard work…
LinkedDoc.Reload (Py)
import clr import sys pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib' sys.path.append(pyt_path) import os import ntpath import System from System.Collections.Generic import * clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager doc = DocumentManager.Instance.CurrentDBDocument app = DocumentManager.Instance.CurrentUIApplication.Application clr.AddReference("RevitNodes") import Revit clr.ImportExtensions(Revit.Elements) clr.ImportExtensions(Revit.GeometryConversion) clr.AddReference("RevitAPI") from Autodesk.Revit.DB import * ###################### DEFINITIONS ######################## # Convert to list should we need to... def tolist(obj1): if hasattr(obj1,"__iter__"): return obj1 else: return [obj1] # turns a list of 1 item into a list of equal length to another list... def MatchSingletonListLength(l1, l2): arr = [] if len(l1) == 1 and len(l2)>1: i = 0 while i<len(l2): arr.append(l1[0]) i = i+1 return arr # Creates a model path given a list of directories to search and a model name to find... def CreateModelPath(_dirs, _mName): _mPath = None for dir in _dirs: fp = "" # loop through each file in the directory... for file in os.listdir(dir): # check if... # a) it ends with .rvt # b) that it contains the fname we have specified # c) doesn't have more that two "." as this would be a backup file if you have a workshared model. # then if these conditions are met it will create a full filepath from the directory and the file that was found. if file.endswith(".rvt") and _mName in file and file.count(".") == 1: _mName = file fp = os.path.join(dir, _mName) # We are stopping the loop at the first satisfactory occurance where conditions are met. break # we will double check if this file exists first... if os.path.exists(fp): # and now convert our filepath string to a ModelPath, this is Path to another Linked file in Revit. _mPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(fp) # Break out of the directory loop as we have found the model we want... break return _mPath # Gets the linked models name removing the extension... def GetModelNameFromLinkedDoc(_lDoc): efr = _lDoc.GetExternalFileReference() fPath = ModelPathUtils.ConvertModelPathToUserVisiblePath(efr.GetPath()) fName = ntpath.basename(fPath) if ".rvt" in fName: _mName = fName[:-4] else: _mName = fName return _mName # Parse the ModelPath to get the model name as a string... def GetModelNameFromModelPath(_modelName): pathStr = ModelPathUtils.ConvertModelPathToUserVisiblePath(_modelName) name = pathStr.split("\\")[-1] return name ###################### VARIABLES ###################### run = tolist(IN[0])[0] # Linked Docs to reload... lDocs = tolist(UnwrapElement(IN[1])) # Directories to search... dirs = None if IN[2]: dirs = tolist(IN[2]) # Model Name input logic... mNames = None if not IN[3] == None: # if model names are given then strip the filetype from them so we can search for them in the directories as they may have a Rev. arr = [] names = tolist(IN[3]) for n in names: if ".rvt" in n: arr.append(n[:-4]) else: arr.append(n) mNames = arr else: # if no model names given then use the linked docs given to populate the model names list. This would effectively be a simple ReloadFrom rather than the option to reload a model with a completely different name. mNames = [GetModelNameFromLinkedDoc(lDoc) for lDoc in lDocs] # WorksetConfigurationOption input logic... opts = None if not IN[4] == None: if not len(tolist(IN[4])) == len(lDocs): #if given option count doesn't match the count of linked docs then make them the same by taking the first option and making the counts equal. opt = [tolist(IN[4])[0]] opts = MatchSingletonListLength(opt,lDocs) else: # if none there are options and they match the count of linked docs, then wrap in a list just in case there is only one in all inputs. opts = tolist(IN[4]) else: # if no option given, then default to OpenAllWorksets arr = [] i = 0 while i < len(lDocs): arr.append(WorksetConfigurationOption.OpenAllWorksets) i = i+1 opts = arr ####################### MAIN SCRIPT ######################## if run: outList = [] if lDocs: if dirs: # Check if the necessary inputs are of equal length... if len(lDocs) == len(mNames) == len(opts): for lDoc,mName,opt in zip(lDocs,mNames,opts): arr = [] # try and find the model to be linked in the given directories... mPath = CreateModelPath(dirs,mName) # if model has been found then try ReloadFrom... if mPath: try: arr = [] # Construct a new WorksetConfig with the specified options... wkStCfg = WorksetConfiguration(opt) # Reload model and get result... loadRes = lDoc.LoadFrom.Overloads.Functions[1](mPath,wkStCfg) # report result as Output... arr.append(GetModelNameFromModelPath(loadRes.GetModelName()) + ":" + loadRes.LoadResult.ToString()) arr.append(ModelPathUtils.ConvertModelPathToUserVisiblePath(mPath)) except Exception,e: arr.append("Could not load " + mName + "\n" + e.message) else: # if not model could be found in any of the directories given... arr.append("Could not find " + mName + ".rvt in Directory given") outList.append(arr) OUT = outList else: # if for some reason the list lengths do not match... OUT = "List lengths of the inputs do not match." # if directory not given then a standard reload will be done for all linked docs given... else: for lDoc in lDocs: arr = [] try: loadRes = lDoc.Load() # report result as Output... arr.append(GetModelNameFromModelPath(loadRes.GetModelName()) + ":" + loadRes.LoadResult.ToString()) except Exception,e: arr.append("Failed to Load:-\n" + e.message) outList.append(arr) OUT = outList else: OUT = "No Linked Documents given" else: OUT = "Please set Run to True"
Note: we are handling optional inputs quite a bit in the code above, this is to make this node multi-functional. Here is a breakdown of each optional input:-
- modelDirectory_Opt – If no model directory given, then a standard Reload() will be done. Else, a ReloadFrom() will be done from any files found in the given directory/directories.
- modelName_Opt – If no model names are given, then the current linked document name will be used search the directory/directories given.
- worksetConfigOpts_Opt – If not WorksetConfigurationOption is given, then the default WorksetConfigurationOption will be “OpenAllWorksets”.
Also Note: We are also checking the input list lengths and ensuring that they match the number of Linked Documents given since we are using Pythons zip method to iterate all lists at once. I love this method.
This is my method for Reloading Linked Models through Dynamo, but I am sure there are plenty of variations out there with more or less features.
Anyway, have a play with it. I hope this helps you either create a better script or use this in your own workflows to save you that little bit of hassle Reloading Models on your next Model Exchange. 🙂
EDIT:
I have updated this post just a touch. Basically, the method used before was relying upon collecting RevitLinkInstances to get the linked model name and path e.t.c. and using this as my LinkedDoc input (which is now RevitLinkTypes). This was causing a null if the Revit Link was unloaded meaning I could not Reload or Reload from any Revit Link that was Unloaded. Of course, being overly confident in the nodes ability I forgot to test with links Unloaded <sigh>… rookie mistake, but we all make ’em! This became evident when I was using it on a project where the architects models had been moved to another directory, of course breaking the links. I ran the script, and nothing but nulls!!! Bugger! Should have tested for this! So, I rewrote the Get Revit Links Node so now it gets the RevitLinkTypes rather than instances. Now it works like a charm! 🙂
July 10, 2018 at 4:08 pm
Thanks a lot for these great nodes ,,,
but how to make the node of Revit link “Type ,
do you mind if write a python script of it
thanks a lot
LikeLike
December 26, 2018 at 6:51 am
Hi, when i copy paste your Code from Reload links from says “invalid syntax” can you share your .dyn file? thanks in advance
LikeLike
December 26, 2018 at 7:46 am
Hi Sergio, I will check the code again, but this is most likely the formatting on this WP template. However, the .dyn file is already in this post, try with that…
https://danimosite.wordpress.com/2017/09/24/dynamo-utils-revit-links/
LikeLike
December 27, 2018 at 5:46 am
Thanks for your reply, in that link the .dyn doesnt have the code for RevitLinkType.Reload.
Thanks in advance.
LikeLike
December 27, 2018 at 6:03 pm
Yup, you were right. I have added that in there now. Try again.
LikeLike
June 20, 2019 at 10:23 am
Hi!
really interesting approach, I think you uploaded the script, however I cannot see it , Would you be able to share it please?
Thaks in advance
Jose
LikeLike
July 19, 2019 at 12:41 pm
Hi Jose, can you try this link maybe…
https://www.dropbox.com/s/bj8v2e4a72gkis9/RevitLinkUtils.dyn?dl=0
LikeLike
July 10, 2019 at 11:23 am
Hi,
This is very cool and something I really need in my workflow.
I still don’t understand how to use the modelDirectory_Opt. I want to input my folder (directory) with models so that each link will change path to my approved folder of models. Right now I receive the models with links that are not from my company. I have a folder with all models that I would like to transfer to those links. Is there any way to that?
LikeLike
July 19, 2019 at 12:46 pm
Hi AHA,
Thank, I’m glad you like it! 🙂
The optional Model Directory is where you want to repath the models to, this will search the directory for the Model filename and repath to that directory (Note: this is not recursive, so will not find in sub-folders). Is this not working correctly? If not, I’ll have another look over the code and test.
Cheers,
Dan
LikeLike