Count Group (Model and Details), Instances and Types Without Arrays Counted as Groups

Count Group (Model and Details), Instances and Types Without Arrays Counted as Groups

jmcouffin
Contributor Contributor
294 Views
3 Replies
Message 1 of 4

Count Group (Model and Details), Instances and Types Without Arrays Counted as Groups

jmcouffin
Contributor
Contributor

I have 4 different functions that fetch: 

 

- model group instances count

- model group types count

- detail groups instances count

and you guessed it...

- detail groups types count

 

for model group instances, I go with:

 

 

 

model_groups_instances_ids = (
        DB.FilteredElementCollector(doc)
        .OfCategory(DB.BuiltInCategory.OST_IOSModelGroups)
        .WhereElementIsNotElementType()
        .ToElementIds()
    )
    arrays_groups_ids = [array.IntegerValue for array in q.get_array_group_ids(doc)]
    return len([model_group_id for model_group_id in model_groups_instances_ids if model_group_id.IntegerValue not in arrays_groups_ids])

 

 
where get_array_group_ids is:
 

 

def get_array_group_ids(doc=None):
    """
    Collects and returns the IDs of all array groups in the given document.

    Args:
        document (DB.Document): The Revit document to search for array groups.

    Returns:
        list: A list of element IDs representing the array groups.
    """
    array_list = DB.FilteredElementCollector(doc or DOCS.doc).OfCategory(
        DB.BuiltInCategory.OST_IOSArrays
    )
    arrays_groups = []
    for ar in array_list:
        arrays_groups.extend(ar.GetOriginalMemberIds())
        arrays_groups.extend(ar.GetCopiedMemberIds())
    return set(arrays_groups)

 

 

The count is right in a very simple example file containing arrays I made and groups I made.

In the latest Revit arch sample, for example, the count if off... and I don't get why... 

 

Any pointer appreciated; I spent way too much time on this already.

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

jeremy_tammik
Alumni
Alumni

No idea whether this helps, but I asked Gemini for you. Here is the response from Gemini 1.5:

   

Certainly, let's delve into the issue of accurately counting model and detail group instances and types in Revit, excluding those within arrays.

Understanding the Problem

The provided code snippet for counting model group instances seems to have a fundamental flaw. It relies on checking if the model group ID is not present in the arrays_groups_ids list, which might not accurately exclude groups within arrays.

Key Considerations

  1. Array Membership: Determining whether a group is truly within an array is more complex than simply checking if its ID is present in the array's member IDs. A group might be a member of an array, but it could also be placed independently elsewhere in the model.
  2. Array Types: Revit arrays can have different behaviors. Some arrays might "own" their members, while others might simply reference them.
  3. Performance: Efficiently iterating through large numbers of elements and checking their relationships within arrays can significantly impact performance.

Revised Approach

Here's a revised approach that aims to more accurately count model group instances while excluding those within arrays:

Python
 
from Autodesk.Revit.DB import *

def get_group_instances_count(doc, is_model_group=True):
    """
    Counts the number of group instances in the given document, 
    excluding those that are members of arrays.

    Args:
        doc (Document): The Revit document to search.
        is_model_group (bool): True for model groups, False for detail groups.

    Returns:
        int: The count of group instances.
    """

    collector = (
        FilteredElementCollector(doc)
        .OfCategory(
            BuiltInCategory.OST_IOSModelGroups
            if is_model_group
            else BuiltInCategory.OST_IOSDetailGroups
        )
        .WhereElementIsNotElementType()
    )

    group_instances = list(collector)

    count = 0
    for group in group_instances:
        # Check if the group is a member of any array
        is_array_member = False
        for array in (
            FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_IOSArrays)
        ):
            if group.Id in array.GetOriginalMemberIds() or group.Id in array.GetCopiedMemberIds():
                is_array_member = True
                break

        if not is_array_member:
            count += 1

    return count

# Example usage:
model_group_instance_count = get_group_instances_count(doc, True)
detail_group_instance_count = get_group_instances_count(doc, False)

Explanation

  1. Iterate through Group Instances: The code iterates through each group instance in the document.
  2. Check Array Membership: For each group, it checks if the group's ID is present in the GetOriginalMemberIds()or GetCopiedMemberIds() of any array in the document.
  3. Count Non-Array Members: If the group is not found within any array, the count is incremented.

Key Improvements

  • Direct Array Membership Check: This approach directly checks if the group is a member of any array, providing a more accurate exclusion.
  • Readability and Maintainability: The code is more organized and easier to understand.
  • Flexibility: The function can be easily adapted to count both model and detail group instances.

Further Considerations

  • Performance Optimization: For large models, consider optimizing the array membership checks using more efficient data structures or algorithms.
  • Array Types: If you need to handle specific array types or behaviors, you might need to refine the array membership check accordingly.

Disclaimer: This revised code provides a more robust approach to counting group instances while excluding those within arrays. However, it's essential to thoroughly test it in various scenarios and with different Revit models to ensure its accuracy and reliability in your specific use cases.

I hope this revised approach and explanation are helpful! Feel free to ask if you have any further questions or require more specific guidance.

   

Gemini 2.0 is sometimes a bit smarter:

   

Let's dive into this Revit API challenge. You're on the right track, but there's a subtle nuance with how Revit handles groups within arrays that needs addressing. The issue stems from the fact that GetOriginalMemberIds and GetCopiedMemberIds return the IDs of group instances, not the group types. When a group is part of an array, Revit treats each instance within the array as a separate entity, even though they share the same group type. Your current approach counts each of these instances, leading to overcounting, especially in complex models like the Revit Architecture sample.

Here's a refined approach that leverages the group type to ensure accurate counts, even within arrays:

Python
 
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit import DB

def count_groups_and_instances(doc):
    """Counts model and detail group instances and types, handling arrays correctly."""

    group_counts = {
        "model_instance_count": 0,
        "model_type_count": 0,
        "detail_instance_count": 0,
        "detail_type_count": 0
    }

    for bic in [DB.BuiltInCategory.OST_IOSModelGroups, DB.BuiltInCategory.OST_DetailGroups]:
        group_instances = DB.FilteredElementCollector(doc).OfCategory(bic).WhereElementIsNotElementType().ToElements()
        group_types = set()  # Use a set to store unique group type IDs

        for instance in group_instances:
            group_type_id = instance.GetTypeId()
            if group_type_id not in group_types:
                group_types.add(group_type_id)

                # Check if this instance is part of an array. If not, increment instance count.
                if not is_instance_in_array(doc, instance.Id):
                    if bic == DB.BuiltInCategory.OST_IOSModelGroups:
                        group_counts["model_instance_count"] += 1
                    else:
                        group_counts["detail_instance_count"] += 1
            
        if bic == DB.BuiltInCategory.OST_IOSModelGroups:
            group_counts["model_type_count"] = len(group_types)
        else:
            group_counts["detail_type_count"] = len(group_types)

    return group_counts


def is_instance_in_array(doc, instance_id):
    """Checks if an element instance is part of an array."""
    array_list = DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_IOSArrays).ToElements()
    for array in array_list:
        member_ids = array.GetAllMemberIds() # Use GetAllMemberIds for both original and copied members
        if instance_id in member_ids:
            return True
    return False


# Example usage:
counts = count_groups_and_instances(doc)
print(counts)

Key Improvements and Explanations:

  1. Directly Working with Group Types: The code now collects group instances initially but uses instance.GetTypeId() to get the corresponding group type ID. This allows us to count unique group types, preventing overcounting due to arrays.

  2. is_instance_in_array Function: This function efficiently determines if a given group instance is part of an array by checking both original and copied member IDs. It uses array.GetAllMemberIds() which is more efficient and robust.

  3. GetAllMemberIds(): This method of the Array class is crucial. It retrieves all member IDs (both original and copied) within the array. This ensures that we correctly identify instances within arrays, regardless of whether they are original or copied. Your previous approach of using separate GetOriginalMemberIds() and GetCopiedMemberIds() and then using extend and set was inefficient.

  4. Clearer Counting Logic: The code now directly increments the instance count only if the instance is not part of an array. This logic is more explicit and avoids the need for list comprehensions and set operations for instance counting, making it more performant.

  5. Combined Function: The code consolidates the counting logic for model and detail groups into a single function, making it more organized and reusable.

Practical Demonstration:

  1. Revit Architecture Sample: Run this revised code on the Revit Architecture sample model. You should now get accurate counts for model and detail group instances and types, even those within arrays.

  2. Custom Models: Test with your own models, especially those containing complex array arrangements. The is_instance_in_array function, combined with the focus on group types, should handle these scenarios correctly.

This approach provides a more robust and accurate solution to your group counting problem. It directly addresses the issue of arrays by focusing on group types and efficiently identifying instances within arrays. This will give you the correct counts you're looking for, even in complex Revit models.

   

I'll leave it up to you to evaluate and either discard as hallucinated junk or delve in deeper by correcting mistakes and asking for further clarification. I hope this can provide a starting point at least. Please let me know whether it helps and how you end up solving this. Thank you!±

    

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

jmcouffin
Contributor
Contributor

Eval: Terrible... 
but I am not surprised by LLMs here

What I need is not so much the code but someone with an understanding of the insides of Revit and its API, something that is not clearly exposed.

my understanding and some facts after a few tests:
- Arrays create groups

- Removing the array from the model does not removes it from the DB

- Therefore, to identify its usage: array.OwnerViewId is a way to find out, if the InvalidElementId comes up

- Groups used in an array will still be considered as members, whatever you do to the array

- Not sure, here, as I could not reproduce after noticing, but other tools than arrays can create groups _ can't remember which one...

 

The code I came up with, but I am still not 100% confident with the results:

 

def count_groups(doc):
    """
    Counts the instances and types of model and detail groups in a Revit document, excluding array members.
    
    Args:
        doc (DB.Document): The Revit document to process.
    
    Returns:
        tuple: A tuple containing four integers:
            - model_group_instances_count (int): The count of model group instances.
            - model_group_types_count (int): The count of model group types.
            - detail_group_instances_count (int): The count of detail group instances.
            - detail_group_types_count (int): The count of detail group types.
    """
    arrays_members_collector = DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_IOSArrays).ToElements()
    arraysmembers = set()
    for array in arrays_members_collector:
        if array.OwnerViewId != DB.ElementId.InvalidElementId:
            arraysmembers.update(array.GetOriginalMemberIds())
            arraysmembers.update(array.GetCopiedMemberIds())

    arrays_grouptype_members = set(doc.GetElement(array).GetTypeId() for array in arraysmembers)


    model_groups = DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_IOSModelGroups).ToElements()
    model_group_instances_count = 0
    model_group_types_count = 0
    for model_group in model_groups:
        if type(model_group) is DB.GroupType and model_group.Id not in arrays_grouptype_members:
            model_group_types_count += 1
        elif type(model_group) is DB.Group and model_group.Id not in arraysmembers:
            model_group_instances_count += 1

    detail_groups = DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_IOSDetailGroups).ToElements()
    detail_group_instances_count = 0
    detail_group_types_count = 0
    for detail_group in detail_groups:
        if type(detail_group) is DB.GroupType and detail_group.Id not in arrays_grouptype_members:
            detail_group_types_count += 1
        elif type(detail_group) is DB.Group and detail_group.Id not in arraysmembers:
            detail_group_instances_count += 1
    return model_group_instances_count, model_group_types_count, detail_group_instances_count, detail_group_types_count

 

 

 
0 Likes
Message 4 of 4

jeremy_tammik
Alumni
Alumni

Always aim for KISS! Good luck evaluating and simplifying.

   

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