Replace Family Parameters with shared parameters

Hi everybody, is it possible to have some help for the following subject :
I’m trying to use the FamilyDocReplaceParameter node from the crumple Package from @GavinCrump to replace family parameters with shared parameters.

I don’t understand why I have three nulls at the end of this script and why it’s not changing anything in the current family doc document

( for more info of what’s coming next :

  • It’s a script that I would like to use with Bird Tools dynamo multiplayer
  • I would like to replace the Code block that gives parameters name to replace and to add by an excel import)

Ok it was my Mistake, I will share the correct script tomorrow because it works like a charm with the right data flow !

Give this version a spin - I revisited this workflow and really tightened it up beyond my old tutorial.

1 Like

Thank you Gavin !
I will look after your new workflow soon

If someone wants to make the same thing one day, here is the right script/graph (the excel import is still missing but the replacement works)

1 Like

@GavinCrump
Thanks for being so generous and sharing the script :slight_smile:
I am trying this script on Revit 2024.

I tried to use the ForgeTypeId in place of BuiltInParameterGroup as the former has become Obsolete in Revit 2024 API.

Error: PythonEvaluator.Evaluate operation failed. Traceback (most recent call last): File , line 54, in . Exception: Cannot replace a parameter of a different ParameterType. Parameter name: familyDefinition

Python Code:


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

# Boilerplate text
import clr

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

clr.AddReference("RevitAPI")
import Autodesk 
from Autodesk.Revit.DB import *

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

param_lists_out = []
counter = 0

# Main body of code
for doc,plst,dlst in zip(docs,param_lists,def_lists):
	
	param_list_out = []
	
	for p,d in zip(plst,dlst):
	
		# start a transaction
		trans = Transaction(doc, 'Replace parameters')
		trans.Start()
		
		# Set the counter for naming parameters
		counter += 1
		strCount = str(counter)
		tempName = "Temporary " + strCount
		
		# Get family manager and parameter settings
		fm = doc.FamilyManager
		pg = p.Definition.GetGroupTypeId()
		pi = p.IsInstance
		
		# If parameter is shared, replace with family
		# If not, then we rename it anyway
		if p.IsShared:
			p = fm.ReplaceParameter(p, tempName, pg, pi)
		else:
			fm.RenameParameter(p, tempName)
		
		# Replace family parameter with shared parameter
		shp = fm.ReplaceParameter(p, d, pg, pi)
		param_list_out.append(shp)
		
		# Close the transaction
		trans.Commit()
		TransactionManager.Instance.ForceCloseTransaction()
	
	param_lists_out.append(param_list_out)

# Preparing output to Dynamo
OUT = [param_lists_out,docs]

Maybe I am doing a mistake somewhere.
Can @GavinCrump or other Dynamo Champions offer some help? :slight_smile:

Many thanks in advance!

Welcome to the fun of API deprecation! I haven’t bothered updating my code in Crumple yet as I’m generally not in 2024 at this point that often and build against 2022 for now. Personally I’m not a fan of the spec/group type calling approach. I understand it, but really wish it had a clearly documented enum (I assume it will be coming on APS docs). If anyone knows of one, feel free to drop it in this thread.

If you search forgetype and spectype by name on the forums you will find some examples of how to feed in a human readable name and get an equivalent spec/group type. Here’s one example:

You’ll need to add some code into my previous script to get these forgetypeIds instead of my approach which uses phased out methods/groups etc. in 2024+

1 Like

@GavinCrump
Thanks for your reply :slight_smile:

I edited the script a bit using GPTs.


# Boilerplate text
import clr

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

clr.AddReference("RevitAPI")
import Autodesk 
from Autodesk.Revit.DB import *

# Unwrap all elements to use with the API
docs = IN[0]
param_lists = IN[1]
def_lists = IN[2]

param_lists_out = []
counter = 0

# Main body of code
for doc, plst, dlst in zip(docs, param_lists, def_lists):
    param_list_out = []
    
    for p, d in zip(plst, dlst):
        # Start a transaction
        trans = Transaction(doc, 'Replace parameters')
        trans.Start()
        
        # Set the counter for naming parameters
        counter += 1
        strCount = str(counter)
        tempName = "Temporary " + strCount
        
        # Get family manager and parameter settings
        fm = doc.FamilyManager
        pi = p.IsInstance
        
        # Check if parameter types match
        if p.Definition.GetDataType() == SpecTypeId.String.Text:
            # Replace the parameter
            shp = fm.ReplaceParameter(p, d, pi)
            param_list_out.append(shp)
        else:
            # Handle the type mismatch (e.g., log a warning or skip the replacement)
            # You can customize this part based on your requirements
            print("Parameter type mismatch: {} ({}) and {} ({})".format(p.Definition.Name, p.Definition.GetDataType(), d.Definition.Name, d.Definition.GetDataType()))
        
        # Close the transaction
        trans.Commit()
        TransactionManager.Instance.ForceCloseTransaction()
    
    param_lists_out.append(param_list_out)

# Prepare the output for Dynamo
OUT = [param_lists_out, docs]

But still doesn’work .
Shows error at Line 47: PythonEvaluator.Evaluate operation failed. Traceback (most recent call last): File"", line 47, in Attribute Error: ‘ExternalDefinition’ object has no attribute ‘Definition’

I generally don’t work with GPT code as it’s typically nonsensical or doesn’t produce outcomes suitable for complex problems like this one - it just creates more problems to fix unless it’s a basic problem or the querier has some understanding of the code it returns. Hopefully someone gets time to write functioning code in future here, unfortunately due to time constraints it likely wont be me currently.

GPT stands for Generative Pre-trained Transformer, but as the datasets are almost all generic in nature they likely should stand for Generic Pre-Trained Transformer.

What this means is that any code you get from it will be a pre-determined result from: it’s lifespan history (minimal in a per user context), a small sampling of relevant data, and a PLETHORA of irrelevant data. This is doubly the case as most (nearly all) gpts are trained on data which predates the last 3 releases of Revit, and therefore all of those API changes are going to cause it to fail. And even for stuff which works it’s usually going to make stuff up as the Revit API isn’t as common as other languages which are out there. That small percentage of Revit API on the web has massive impacts on the outcomes.

As an example I once spent 30 minutes trying to get it to draw a wall as an experiment only to get sample which thought things like points existed in the API. After 30 minutes I gave up and said “would this be easier with a google search?” And so in an new incognito browser I tried the ‘I’m feeling lucky’ google result and found the functional code sample after scrolling down 3 pages. Sadly googles results are becoming SEO’d to death even for API calls now (thanks to GPT), so that’s becoming less reliable. And so everyone is best off learning the API.

A GPT can certainly help with learning (well suited for this actually), but don’t expect to get functional code and always test the results thoroughly.

Hi,

in addition to what has been said above, it is preferable to manage the entire process of changing parameters in a single Python node and to open and close families successively, rather than opening all families and ending up with several dozen (or even a hundred) documents open.

1 Like

I found a brief window to look into this further, I think you had the workflow right before when you replaced the group with GetGroupTypeId() instead of ParameterGroup.

Your warning seems to indicate that you’re trying to change a parameter’s type, which is not permitted in Revit API or Revit itself (e.g. replacing an integer type parameter with a number). Cross check that isn’t the case.

To add to this I can see forgetypeIds are going to be on my radar so I bit the bullet and figured out how to get specs/groups by name. Much easier than I thought, I quite like them now vs the old method - targeting the human friendly name is great.

# Boilerplate text
import clr

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

clr.AddReference("RevitAPI")
import Autodesk 
from Autodesk.Revit.DB import *

# Current doc/app/ui
doc = DocumentManager.Instance.CurrentDBDocument

# Preparing input from dynamo to revit
specs_get  = IN[0]
groups_get = IN[1]

# Get forgetypeids and names
spec_ids   = SpecUtils.GetAllSpecs()
spec_names = [LabelUtils.GetLabelForSpec(s) for s in spec_ids]

# Get grouptypeIds and names
group_ids = ParameterUtils.GetAllBuiltInGroups()
group_names = [LabelUtils.GetLabelForGroup(g) for g in group_ids]

# Function: Return element by name match
def element_byNameMatch(n, elements, names):
    if n in names:
        ind = names.index(n)
        return elements[ind]
    else:
        return None

# Return forgetypeIds
OUT = [ [element_byNameMatch(n, spec_ids, spec_names) for n in specs_get],\
        [element_byNameMatch(n, group_ids, group_names) for n in groups_get] ]

Get forgetypes by name.dyn (6.4 KB)