Add Shared Parameters

Title: Issues with Adding Shared Parameters to Generic Model In-Place Families in Revit

Hello everyone,

I’m currently working on a Revit project where I need to add shared parameters to selected Generic Model In-Place Families. I’m using Python scripts with the Revit API, and the process involves selecting specific in-place families and adding custom shared parameters from a predefined shared parameters file.

The script works fine for adding the parameters, but I’ve encountered an unexpected issue:

Problem: The script is not only applying the parameters to the selected in-place families but also appears to be affecting future in-place families. I believe this is because the script modifies the document’s ParameterBindings, which applies to all instances of that category (Generic Model). This means any new in-place family created after running the script inherits these shared parameters, which is not the intended behavior.

What I Need:

  • I want the script to only add the parameters to the selected generic model in-place families, without affecting any new families created afterward or any other model-in-place family

If anyone has experience handling in-place families or dealing with shared parameters in a way that only affects specific instances, your guidance would be much appreciated!

Thank you in advance for your help!

Best regards,
Dhru

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from Autodesk.Revit.Exceptions import InvalidOperationException
from Autodesk.Revit.UI.Selection import ObjectType
import os, time
from pychilizer import database, units, select, geo
from pyrevit import forms, script, revit, DB
from dfl_matrix.load import main as load_main
from dfl_matrix.unload import main as unload_main
from dfl_matrix.folderpath import main as folderpath_main

# Initialize script output
output = script.get_output()

load_main()
output.print_md("**Script Initiated** : Add DFL_Shared Parameters")

# Define the mapping of parameter groups
parameter_groups = {
    "Text": BuiltInParameterGroup.PG_TEXT,
    "Identity Data": BuiltInParameterGroup.PG_IDENTITY_DATA,
    "Electrical": BuiltInParameterGroup.PG_ELECTRICAL,
    "Plumbing": BuiltInParameterGroup.PG_PLUMBING,
    "Other": BuiltInParameterGroup.PG_GENERAL,
}

def add_shared_parameter_to_in_place_family(doc, element, parameter_name, newDef, parameter_group, prefill_value=None):
    try:
        # Check if the parameter already exists
        param = element.LookupParameter(parameter_name)
        if param:
            output.print_md("Parameter '%s' already exists." % parameter_name)
            return
        
        # Create a category set and add the category of the element
        category_set = doc.Application.Create.NewCategorySet()
        category_set.Insert(element.Category)

        # Create an instance binding for the parameter
        instanceBinding = doc.Application.Create.NewInstanceBinding(category_set)

        # Determine the parameter group
        group = parameter_groups.get(parameter_group, BuiltInParameterGroup.PG_DATA)

        # Add the parameter to the document under the specified group
        bindingMap = doc.ParameterBindings
        bindingMap.Insert(newDef, instanceBinding, group)

        # Re-fetch the parameter after adding it
        param = element.LookupParameter(parameter_name)

        # Prefill the parameter with a value if provided and applicable
        if param and prefill_value is not None:
            if param.StorageType == StorageType.String:
                param.Set(prefill_value)
            elif param.StorageType == StorageType.Double:
                param.Set(UnitUtils.ConvertToInternalUnits(float(prefill_value), DisplayUnitType.DUT_WATTS))
            elif param.StorageType == StorageType.Integer:
                param.Set(int(prefill_value))
            else:
                output.print_md("Unsupported storage type for prefill value for '%s'." % parameter_name)

        output.print_md("Parameter '%s' successfully added." % parameter_name)

    except Exception as e:
        output.print_md("An error occurred while adding '%s': %s" % (parameter_name, str(e)))


def main():
    doc = revit.doc

    # Start timing the script
    start_time = time.time()

    # Use pychilizer's selection method to select Generic Model In-Place Families
    selection = select.select_with_cat_filter(DB.BuiltInCategory.OST_GenericModel, "Select Generic Model In-Place Families")
    
    # Filter the selection for only In-Place Families
    in_place_families = [elem for elem in selection if hasattr(elem, 'Symbol') and elem.Symbol.Family.IsInPlace]
    
    if not in_place_families:
        forms.alert("No Generic Model In-Place Families selected.", exitscript=True)
        return

    # Function to define folder paths (assuming this function exists)
    DIRECTORY, DFLFAMILYLIBRARY_FOLDER, DFL2020_FOLDER = folderpath_main()
        
    # Dynamically construct the shared parameters file path
    shared_params_file_path = os.path.join(
        DIRECTORY,
        "5. REVIT",
        "DFL_Shared_Parameter_File",
        "DFL_Family_Parameters.txt"
    )
    output.print_md("**Shared parameters file path** : %s" % shared_params_file_path)

    # Load the shared parameters file once
    app = doc.Application
    app.SharedParametersFilename = shared_params_file_path
    sharedParamsFile = app.OpenSharedParameterFile()

    if not sharedParamsFile:
        output.print_md("No shared parameter file found.")
        return

    # Specify the parameters to add and prefill values
    parameters_to_add = [
        {"name": "Equipment Code", "group": "Text", "prefill": None},
        {"name": "Equipment ID", "group": "Text", "prefill": None},
        {"name": "Spa Feature", "group": "Text", "prefill": None}
    ]

    # Start a single transaction for all operations
    t = Transaction(doc, "Add Shared Parameters to In-Place Families")
    
    try:
        t.Start()

        # Add each parameter to each selected in-place family
        for param_data in parameters_to_add:
            # Find the parameter definition in the shared parameters file
            newDef = None
            for defGroup in sharedParamsFile.Groups:
                for defDef in defGroup.Definitions:
                    if defDef.Name == param_data["name"]:
                        newDef = defDef
                        break
                if newDef:
                    break

            if not newDef:
                output.print_md("Parameter definition for '%s' not found in shared parameter file." % param_data["name"])
                continue

            for element in in_place_families:
                add_shared_parameter_to_in_place_family(doc, element, param_data["name"], newDef, param_data["group"], param_data["prefill"])

        # Commit the transaction
        if t.GetStatus() == TransactionStatus.Started:
            t.Commit()

    except InvalidOperationException as ioex:
        output.print_md("Operation was cancelled or an error occurred: %s" % str(ioex))
        if t.GetStatus() == TransactionStatus.Started:
            t.RollBack()

    except Exception as ex:
        output.print_md("An unexpected error occurred: %s" % str(ex))
        if t.GetStatus() == TransactionStatus.Started:
            t.RollBack()

    finally:
        # Ensure this block runs even if the transaction fails
        end_time = time.time()
        execution_time = end_time - start_time
        output.print_md("Execution Time: %.2f seconds" % execution_time)


# Run the main function in a try-finally block
try:
    main()
finally:
    # Ensure the unload function is always called after main execution
    unload_main()

Hi and welcome,

basically, you cannot, not like this.
This is the whole issue with inplace families.
to be able to add a shared param to a family, it needs to be a family! #ouroboro.
One way to tackle this is not to use in place fam in the first place.
You could use the pychilizer extension that has a script to take the volumes of your inplace fam and paste it in a generic model family template, making it a family.

:point_up: how many times have I written the word family :stuck_out_tongue:

from there, you can then edit the family and add your parameter
doc.EditFamily()

Thanks so much for the response, I changed the code to first convert it to loadable family and then apply shared parameters, it worked like a charm✨

1 Like