Dynamo Script Compatibility Issue for Wall Penetrations

Dynamo Script Compatibility Issue for Wall Penetrations

nikhilvenugopal11
Community Visitor Community Visitor
903 Views
3 Replies
Message 1 of 4

Dynamo Script Compatibility Issue for Wall Penetrations

nikhilvenugopal11
Community Visitor
Community Visitor

Our team has found a Dynamo script to automate the creation of wall penetrations for MEP services in Revit. The script works effectively in Revit 2021 but fails to function in other Revit versions.

Could anyone provide insights on:

  • How to ensure Dynamo scripts are version-independent?
  • Best practices for handling family type dependencies in Dynamo?
  • Any specific nodes or workarounds we should consider for broader compatibility?

We’d appreciate any advice, suggestions, or similar experiences from the community.

 

0 Likes
904 Views
3 Replies
Replies (3)
Message 2 of 4

jeremy_tammik
Alumni
Alumni

Question 1: version independency:

 

You're facing a common challenge with Dynamo scripts and Revit version compatibility. Dynamo scripts can be sensitive to API changes between Revit versions. Here's a breakdown of how to improve version independence:

Understanding the Problem:

Dynamo relies on Revit API nodes. These nodes are wrappers around Revit API functions. When the Revit API changes (e.g., methods are renamed, parameters are added/removed, classes are restructured), the corresponding Dynamo nodes can break or behave unexpectedly. This is the primary reason why a script working in Revit 2021 might fail in other versions.

Strategies for Version-Independent Dynamo Scripts:

  1. Use Core Dynamo Nodes Whenever Possible:

    Prefer built-in Dynamo nodes (from the core libraries like Core, BuiltIn, Geometry) over custom nodes or packages whenever feasible. Core nodes are more likely to be updated for compatibility across Dynamo versions.

  2. Minimize API-Specific Nodes:

    Avoid using nodes that directly expose Revit API elements or methods unless absolutely necessary. These are the most likely to break between Revit versions. For example, avoid nodes that directly create Wall objects using specific constructors if there are higher-level Dynamo nodes that achieve the same result.

  3. Use Python Scripting with Version Checking:

    This is the most robust approach. Embed Python scripts within your Dynamo graph to handle version-specific logic.

    • Get Revit Version: Use the __revit__.Application.VersionName property within your Python script to determine the Revit version.

    • Conditional Logic: Use if/elif/else statements in your Python script to execute different code blocks based on the Revit version. This allows you to use appropriate API calls for each version.

    Example (Python within Dynamo):

    Python
     
    import clr
    
    # Import Revit API classes
    clr.AddReference('RevitAPI')
    from Autodesk.Revit.DB import *
    
    # Get Revit version
    revit_version = __revit__.Application.VersionName
    
    # Input elements (e.g., walls, ducts)
    elements = IN[0]
    #Input family symbol
    familySymbol = UnwrapElement(IN[1])
    
    # Output list
    out_list = []
    
    if revit_version == "2021":
        # Revit 2021 specific code (e.g., using specific API calls)
        for element in elements:
            try:
                #Example: Create opening by face (adapt to your penetration logic)
                host = UnwrapElement(element)
                face = HostObjectUtils.GetSideFaces(host, ShellLayerType.Exterior).First()
                opening = doc.Create.NewOpening(host, face, XYZ.Zero, XYZ.BasisX, XYZ.BasisY)
                out_list.append(opening)
            except:
                out_list.append("Opening creation failed on this element")
    
    elif revit_version == "2022":
        # Revit 2022 specific code (if different API calls are required)
        for element in elements:
            try:
                #Example: Create opening by face (adapt to your penetration logic)
                host = UnwrapElement(element)
                face = HostObjectUtils.GetSideFaces(host, ShellLayerType.Exterior).First()
                opening = doc.Create.NewOpening(host, face, XYZ.Zero, XYZ.BasisX, XYZ.BasisY)
                out_list.append(opening)
            except:
                out_list.append("Opening creation failed on this element")
    
    elif revit_version == "2023":
        # Revit 2023 specific code (if different API calls are required)
        for element in elements:
            try:
                #Example: Create opening by face (adapt to your penetration logic)
                host = UnwrapElement(element)
                face = HostObjectUtils.GetSideFaces(host, ShellLayerType.Exterior).First()
                opening = doc.Create.NewOpening(host, face, XYZ.Zero, XYZ.BasisX, XYZ.BasisY)
                out_list.append(opening)
            except:
                out_list.append("Opening creation failed on this element")
    
    else:
        # Code for other versions or a default fallback
        out_list.append("Script not compatible with this Revit version.")
    
    OUT = out_list
    

    Key Improvements:

    • Version Detection: Explicitly gets the Revit version.
    • Conditional Execution: Uses if/elif/else to execute version-specific code.
      • Example for creating openings: This example uses HostObjectUtils.GetSideFaces and doc.Create.NewOpening which are available in Revit 2021 and later.
    • Error Handling: Includes try/except blocks to catch potential errors and provide informative output.
    • Clearer Structure: The code is organized for better readability and maintainability.
    • Unwrapping Elements: Correctly unwraps Dynamo elements to Revit API elements using UnwrapElement().
  4. Package Management:

    If you must use custom packages, keep them up-to-date. Package developers often release updates to maintain compatibility with new Revit versions.

  5. Testing:

    Thoroughly test your Dynamo scripts in all Revit versions you intend to support. This is crucial for identifying and fixing compatibility issues.

Example Scenario (Wall Penetrations):

Let's say the way you create wall penetrations changed slightly between Revit 2021 and 2022. In 2021 you were using a specific method that was deprecated in 2022. By using the Python scripting approach, you can have one code block that uses the 2021 method when running in Revit 2021 and another code block that uses the new 2022 method when running in Revit 2022.

Important Considerations:

  • API Changes Documentation: Refer to the "What's New" sections in the Revit API documentation for each version to understand the changes that might affect your scripts.
  • Backward Compatibility: While striving for forward compatibility is important, sometimes backward compatibility (making a script work in older versions) might be very difficult or impossible due to significant API changes.

By implementing these strategies, you can significantly improve the version independence of your Dynamo scripts and reduce the maintenance effort required when upgrading Revit. The Python scripting approach with version checking is the most effective way to handle API differences and ensure your scripts work reliably across multiple Revit versions.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 4

jeremy_tammik
Alumni
Alumni

Question 2: family type dendencies:

   

You're hitting on a crucial aspect of Dynamo scripting, especially when dealing with families: managing family type dependencies. When a Dynamo script relies on specific family types, it can easily break if those types are not available in the project or if their names or parameters change. Here's a breakdown of best practices:

Understanding the Problem:

Dynamo scripts often use nodes like "Family Types," "Family Instance.ByFamilyType," or nodes from custom packages that directly interact with family types. If the required family type:

  • Doesn't exist: The script will fail.
  • Has a different name: The script will fail.
  • Has different parameters: The script might run but produce incorrect results or errors.

Best Practices for Handling Family Type Dependencies:

  1. Input Family Types as Inputs:

    The most fundamental practice is to make the family type an input to your Dynamo script. This allows the user to select the appropriate family type each time they run the script, making it much more flexible.

    • Use the "Select Model Elements" or "Select Family Types" node.
    • This removes the hardcoded dependency on a specific family type name within the script.

    Example: Instead of having a "Family Types" node that directly selects "M_Generic Model," use a "Select Family Types" node and connect its output to the "Family Instance.ByFamilyType" node.

  2. Check for Family Type Existence:

    Before attempting to create instances of a family type, check if it exists in the project. This prevents runtime errors.

    • Use a Python script to check if the doc.GetElement(familyTypeId) returns a valid element.

    Example (Python within Dynamo):

    Python
     
    import clr
    
    clr.AddReference('RevitAPI')
    from Autodesk.Revit.DB import *
    
    doc = __revit__.ActiveUIDocument.Document
    family_type_id = UnwrapElement(IN[0]).Id # Input from "Select Family Types" node
    
    family_type = doc.GetElement(family_type_id)
    
    if family_type:
        OUT = family_type
    else:
        OUT = "Family type not found."
    
  3. Use Family Name and Type Name for Robust Selection (with Fallbacks):

    If you need to automate the selection of a family type (without user input), use both the family name and the type name to find it. This is more robust than relying on just the type name, as type names might change.

    Example (Python within Dynamo):

    Python
     
    import clr
    
    clr.AddReference('RevitAPI')
    from Autodesk.Revit.DB import *
    
    doc = __revit__.ActiveUIDocument.Document
    family_name = IN[0]  # Input: Family name (string)
    type_name = IN[1]    # Input: Type name (string)
    
    collector = FilteredElementCollector(doc)
    collector.OfClass(FamilySymbol)
    
    found_type = None
    for fs in collector:
        family = doc.GetElement(fs.Family.Id)
        if family.Name == family_name and fs.Name == type_name:
            found_type = fs
            break
    
    if found_type:
        OUT = found_type
    else:
        #Fallback if type is not found
        collector.WhereElementIsElementType()
        collector.OfClass(FamilySymbol)
        for fs in collector:
            family = doc.GetElement(fs.Family.Id)
            if family.Name == family_name:
                found_type = fs
                break
        if found_type:
            OUT = "Type not found, but family found, using first type: " + found_type.Name
        else:
            OUT = "Family and Type not found."
    
  4. Parameter Mapping:

    If your script relies on specific parameters within the family type, map these parameters by name rather than index. This ensures that your script still works even if the order of parameters in the family changes.

    • Use LookupParameter() to get parameters by name.
  5. Family Templates:

    Consider using family templates to create the required families in the project before running the Dynamo script. This ensures that the necessary families and types are always available.

  6. Dynamo Packages for Family Management:

    Some Dynamo packages offer nodes specifically designed for family management, such as creating, loading, and modifying families. These can simplify some tasks but be careful about version dependency of the packages themselves.

Example Scenario (Wall Penetrations):

Imagine your script creates penetrations using a generic model family.

  • Bad Practice: Hardcoding the family type name "M_Generic Model" directly in the script.
  • Good Practice: Using a "Select Family Types" node to let the user choose the penetration family.
  • Better Practice: Using the Python script to check for family type existence and providing a fallback if the type is not found, or using the family name and type name to find the type.

Combining Techniques:

For the most robust solution, combine these techniques:

  1. Use "Select Family Types" as the primary input.
  2. Use the Python script to validate the selected family type and provide feedback if it's invalid.
  3. Implement parameter mapping to access family parameters by name.

By following these best practices, you can create Dynamo scripts that are much more resilient to changes in Revit projects and family libraries, making them more reliable and easier to use.

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 4 of 4

jeremy_tammik
Alumni
Alumni

I hope those two explanations help you get started with your task. Good luck! Oh, btw, you might want to raise Dynamo-specific question in the Dynamo forum, rather than here in the pure Revit API one:

  

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes