Adjusting Python node for multiple families as input (sublists)

Hi,

Can anybody help me with adjusting a (python) node from Crumple Package (@GavinNicholls)?
The node is ‘ParameterReplaceShared’ and the total script is from this tutorial from him:

I’m having to replace all of my shared parameters.

I have the script working with one family at the time.
Now I want to adjust the script for batch processing all of my families.

I’m running into a problem with the node ParameterReplaceShared where it reconizes my sublists input (seperate revit families) but the output, i think, is still using the ‘current document’ i have opened.

I believe the python script within that node isn’t written for multiple entries and/or working with sublists.

# Made by Gavin Crump
# Free for use
# BIM Guru, www.bimguru.com.au

#Credit to Joshua Budarick for his assistance with this workflow!
# https://www.linkedin.com/in/joshua-budarick-9bb50b4b/

# Boilerplate text
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

#Inputdoc : Part of script by Andreas Dieckmann
inputdoc = IN[0]

if inputdoc == None:
    doc = DocumentManager.Instance.CurrentDBDocument
elif inputdoc.GetType().ToString() == "Autodesk.Revit.DB.Document":
    doc = inputdoc
else:
	doc = DocumentManager.Instance.CurrentDBDocument

# Functions
def uwlist(input):
    result = input if isinstance(input, list) else [input]
    return UnwrapElement(input)

#unwrap all elements to use with API
params = uwlist(IN[1])
names  = uwlist(IN[2])

params_new, names_new = [],[]

# Collect values
if doc.IsFamilyDocument:

	# "Start" the transaction
	TransactionManager.Instance.EnsureInTransaction(doc)
	
	for p,n in zip(params, names):
		try:
			new = doc.FamilyManager.ReplaceParameter(p, n, p.Definition.ParameterGroup, p.IsInstance)
			params_new.append(new)
			names_new.append(n)
		except:
			params_new.append(None)
			names_new.append(None)

	# "End" the transaction
	TransactionManager.Instance.TransactionTaskDone()

	OUT = [params_new, names]
	
else:
	OUT = ["Document is not a family.",""]

The problem I’m having:

Generally to solve situations like this I turn to Python vs using my custom nodes, many of them are designed to work across one document currently.

This seems to work for replacing shared parameters with others in multiple families. The new parameters cannot be in the document as well or it will fail (I haven’t added all the try/except safeguards), and they must exist by name in the active shared parameter file.

I do not have time to customize it to fringe cases for people, but hopefully it’s helpful anyway. If you’re not familiar with Python these types of workflows are usually a good time to begin learning it as well.

# Made by Gavin Crump
# Free for use
# BIM Guru, www.bimguru.com.au

# Boilerplate text
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

#Inputdoc : Part of script by Andreas Dieckmann
docs = IN[0]

#unwrap all elements to use with API
param_lists = IN[1]
names_lists = IN[2]
def_lists = IN[3]

param_lists_out = []

# Collect values
for doc,plst,nlst,dlst in zip(docs,param_lists,names_lists, def_lists):
	
	param_list_out = []
	
	for p,n,d in zip(plst,nlst,dlst):
		
		# Start a transaction
		trans = Transaction(doc, 'replace parameter 1')
		trans.Start()
		
		# Replace parameter
		fam = doc.FamilyManager.ReplaceParameter(p, n, p.Definition.ParameterGroup, p.IsInstance)
		shp = doc.FamilyManager.ReplaceParameter(fam, d, fam.Definition.ParameterGroup, fam.IsInstance)
		
		param_list_out.append(shp)
		
		# Close a transaction
		trans.Commit()
		TransactionManager.Instance.ForceCloseTransaction()

	param_lists_out.append(param_list_out)

OUT = param_lists_out

Bulk replace paramameters (shared to shared).dyn (51.1 KB)

Sample files here:

@GavinNicholls

Thanks so much, I really appreciate this!
I’ve made it work with my library and i’m currently replacing shared parameters in 600+ rfa files.

1 Like

Just adding that the video for this is now available, hopefully helps people facing a similar challenge in understanding the approach I took:

@jeroen.reynders

1 Like

Hello @GavinCrump,

I’ve been experimenting a bit with your scripts, aiming to change 3 family (non-shared) parameters in 700 families, against shared parameters with the same names as family parameters.

I consolidated everything into a single Python script with 4 inputs: the file path (containing .rfa files), a list of parameter names to be changed, a list of new parameter names, and a list of shared parameters (definitions) from the file. In my case, the old and new parameters have the same names.

I feel like Dynamo is achieving success randomly in this case. For the 2100 parameters described, about 30% were changed on the first run, while the rest weren’t. When I tried the script again on the original dataset, it affected different parameters. If I run the script a second time on the overwritten families, it succeeds on about 20-30% of the remaining parameters.

I encoutered here two types of errors: “Parameter replacement failed.” or “The shared parameter is already in use”.
ChatGPT says it can be caused by not properly closed transactions but can’t give the right solution. I tried to add time.wait(3) in some places but it didn’t help.

Could you (or someone else) take a look?

import os
import clr

clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# Get current document
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

# Input data
path = IN[0]  # Folder path
fam_parameters = IN[1]  # Old parameter names
names_lists = IN[2]  # New parameter names
def_lists = IN[3]  # Shared parameter definitions

# Filter files by .rfa extension
paths = [
    os.path.join(root, file)
    for root, dirs, files in os.walk(path)
    for file in files if file.endswith(".rfa")
]
changed_parameters = []
errors = []

# filter definition list
defs = []
for definition in def_lists:
    if definition.Name in names_lists:
        defs.append(definition)

for path in paths:
    try:
        doc = app.OpenDocumentFile(path)
        parameters = []
        old_parameters = doc.FamilyManager.Parameters

        # Collect parameters that need to be replaced
        for old_parameter in old_parameters:
            if old_parameter.Definition.Name in names_lists:
                parameters.append(old_parameter)

        # Replace parameters
        for p, d in zip(parameters, defs):
            try:
                trans = Transaction(doc, 'Replace parameter')
                trans.Start()
                temp_name = f"{p.Definition.Name}_temp"
                doc.FamilyManager.RenameParameter(p,
                                                  temp_name)  # to avoid name collision between fam and shared parameters
                print(p, p.Definition.Name, d, d.Name, p.Definition.ParameterGroup, p.IsInstance)  # just for check
                shared_param = doc.FamilyManager.ReplaceParameter(p, d, p.Definition.ParameterGroup, p.IsInstance)
                changed_parameters.append(shared_param.Definition.Name)

                trans.Commit()
                TransactionManager.Instance.ForceCloseTransaction()
            except Exception as e:
                errors.append(f"Error replacing parameter in file {path}, {p.Definition.Name}: {e}")

                if trans.HasStarted():
                    trans.RollBack()

        TransactionManager.Instance.ForceCloseTransaction()
        # Save and close the family document
        doc.Close(True)

    except Exception as e:
        errors.append(f"Error processing file {path}: {e}")

OUT = {"Changed Parameters": changed_parameters, "Errors": errors}

Dont consolidate my code and expect troubleshooting if gippity is involved… time to learn python if youre at that point.

I’d recommend just using my code and nodes as provided. I have a script set over on github but i still need to test it for revit 2025 if youre using that, probably wont be for a while unfortunately as time isnt my friend these days.

Note that i wouldnt run this over that may documents. Break the task down into say batches of 100 or so for better results.

1 Like

Hi,

In addition to what has been said,
you should start by checking that the parameters and defs lists are in the same order

1 Like

Hey @GavinNicholls,

I appreciate your response. Just to clarify:

  1. Yes, I used your code as a foundation and adapted it for my needs—isn’t that the whole point? So, it wasn’t question about YOUR node, but generally about transactions. Maybe wrote in false topic. If I recall correctly, you actively encourage experimentation on your YouTube channel, inspiring people to learn and tweak things to better suit their workflows. I wasn’t demanding anything from you, just kindly asking; I simply thought that, as an experienced specialist, you might have some insight into what’s causing the issue.
  2. Yes, Python is exactly the language I used to program this. However, the issue doesn’t seem to stem from Python itself but rather from how Dynamo/Revit handles transactions internally.

Thanks for your suggestion, but as you can see in the screenshot, I used the same list for both inputs, so there’s no chance of the order getting mixed up. Even if it were, in the worst case, I would just get the parameters in the wrong order—not experience the inconsistent behavior I’m seeing.

1 Like

I read this as less of a “don’t copy/edit my code” and more of a “Chat GPT does you no good and I don’t have time to correct the computer” (side note I am going to refer to it as gippity in conversations from now on - thank you for the term @GavinNicholls!)

1 Like

So the issue isn’t with transactions, but with the workflow in Python.

There are as many ways in which a Revit action can fail as there are grains of sand on earth. For now I’ll grab 3 which are relevant to your Python:

  1. If you remove a reference causing a formula to break, causing a dimension to become zero, causing an invalid geometry width.
  2. If you try to make a parameter which already exists, causing the 'shared parameter already exists" warning (this is your second error message).
  3. If you provide a gecko as the input instead of a file path.

Each of these infinite conditions has to be accounted for and handled by your application, or things won’t complete. Looking at the 3 conditions above you could:

  1. Check if the parameter you’re about to delete is used in any other parameter’s formula and replace the formula with one using the new parameter if so.
  2. Check if the GUID of the shared parameter you are adding is in the list of GUIDs for all parameters in the file, and skip adding the parameter if so.
  3. Confirm the directory provided is formatted as a gecko, and stop all operations if so.

Fortunately most of the infinite reasons for failure can be accounted for via other tools - in the case of the gecko we know we’ll only ever get a string as the Directory Path node from Dynamo uses controls available in C# to ensure you’re only ever going to output a string or a null (something your code will also have to handle).

But items 1 and 2 (and many more) will require more robust error handling. Some error handling is done generically via the ‘try / except’ here, which is basically preventing an error on file 3 from causing files 4-N from being processed. But as it stands that is only identifying the issues as they come up, not providing instructions on what to do when an error happens. “Why” did it fail has to be fully identified before you can add the error handling.

Generally speaking try excect isn’t good for processing errors, but for identifying them in this way. The processing and avoiding will require you code up your own error handling. Check if something needs to be done before you do it; if not proceed to the next step. Resolving the ‘wy’ will likely also require reading the full warning message, opening the family in the Revit UI, attempting the actions manually, identifying what goes wrong when you do, and then updating the code to handle that situation accordingly.

One last note: This code is overwriting your family documents directly via the doc.Close(true) line. As such you’re going to be adding multiple failure points into the mix - that is if you process files 1-9 correctly, get an error on 10, and then reprocess all 10 files 1-9 will already have the shared parameter in them… I recommend saving as new files any time you try bulk editing documents (rfa, rvt, dwg, dxf, txt, csv, json…) which are used as templates - you can always go back to the ‘save’ method if need be, but editing your original when you aren’t sure exactly what is happening is never a good idea.

Yes to clarify its mainly a gpt thing. I get daytime code queries of this nature at work already so it tends to trigger me a bit. My code is open so as to allow others to learn from and use it - no issues there.

Mainly a nudge to delve further into python and api fundamentals with an end goal of reducing gpt dependence (at first its ok - its how i got into c#). For all intensive purposes cgpt is a bit loose with dynamo and python - tends to make a bit of stuff up in my experience.

These types of family process’ are quite bespoke to users which is why i ended up building my nodes to work under fairly controlled conditions. They also are a huge pain to test as they can be slow and often put revit into a state where a reboot is needed.

Im back from holiday in a few days so will give it a better look then. Family transactions is always a pain and theres very particular requirements for running them in the main/sub document.

General rule of thumb i find is start and commit transactions as leanly as possible, then close the related document if possible afterwards. I generally avoid the dynamo specific transaction manager for these types of tasks.

Hey guys, where did I say I used ChatGPT to create this code? I tried a few times, but currently, ChatGPT completely fails at handling Dynamo’s documentation, so I don’t use it for coding.

Yes, that’s exactly why I first asked ChatGPT if it could find any errors here.

Exactly, that’s the reason why I modified the workflow you presented in the video. Revit struggles with opening 700 families and keeping them in memory.

That’s right, I’ve managed to detect and partially eliminate cases where a parameter is already shared or doesn’t exist in the file. However, I will deal with that later—first, I need to figure out why, under the exact same conditions (using the same input files), sometimes it works, and sometimes I just get an error like “Parameter replacement failed.”

Don’t take this as an attack, but this is exactly what ChatGPT responses look like. I found them unhelpful, so I turned to you instead, and now I feel like I’m being attacked for it.

Me: I have one set of parameters and one set of files. The script behaves randomly. Sometimes it works on some families, other times it doesn’t—but then it works on different ones.
ChatGPT: Hey, maybe your parameters aren’t in the TXT file? Hey, maybe you’re using the wrong family names? Maybe you have false input?
No, because in that case, it wouldn’t work at all for a given test set of files.

That being said, thank you—I really appreciate your time (even during your vacation! ).

1 Like

In the interim the surest way to see what might be happening is to remove the try except clauses to see what they are allowing to be skipped.

The main things that i experience causing errors are:

  • parameter not in sp file or typo in name of parameter
  • the parameter is already there and cannot be replaced over another one
  • the parameter is used in an arrayed element at an instance level by association

All but the first are Revit limitations.

Transaction cannot be started until another type errors are usually if a transaction is not given a chance to be commited in the same document prior.

I believe your code may need to account for the case that a parameter by name being replaced is already shared as well. Might need to replace shared with family before doing final replacement in which case (revit API cannot rename shared parameters nor can it replace shared with shared in 1 step).

Not trying to attack you, but to give you guidance on ‘why’ it’s failing. As I noted there are infinite reasons for failure which need to be accounted for, and we don’t have your family files so we can’t identify which of them is blocking you. Sadly the error message is no clearer to Gavin or I than it is you. For us to explain the issue one of the following has to happen:

  • Gavin or I or someone else has to find the time in our day to troubleshoot with your files
  • You identify why the code is failing by trying the process manually and seeing where you deviate.

One last option is to try building the graph to work on the active family document instead of background documents. This will give you better error handling. Once that is authored you can leverage something like the Dynamo MultiPlayer from Bird Tools to background process all 700 families and thereby catch more of the specific errors.
Edit: Dynamo Multi Player link: Bird Tools - Dynamo Multiplayer | Revit | Autodesk App Store

1 Like