2. Enumerate and Classify Model Entities by Type

Why enumerate before you clean

Cleanup work in HyperMesh is destructive by nature: deleting a component removes its elements, deleting a property orphans the components that referenced it, and deleting a material can silently break a property's card image. Before any deletion script runs, you need a census of the model — how many entities of each type exist, what they are named, and which ones are candidates for removal. This case builds a single Python script that walks the current HyperMesh session, enumerates every entity type through the HyperMesh Python API, and emits a classification summary you can use as the input to a cleanup plan.

The script is deliberately read-only. It queries, counts, and reports; it never modifies the model. That makes it safe to run on a production assembly at any time, and it gives you a repeatable baseline you can diff against after a cleanup pass to confirm what actually changed.

Entity types and the API surface

HyperMesh organizes model data into a small number of top-level collectors and a large number of element and node records. The Python API exposes each type through a dedicated module, and the query pattern is consistent: you ask a module for its collection, then iterate. The table below lists the types this script covers, the module that owns them, and the collector query used to fetch them.

Entity type API module Query used Cleanup relevance
Components hm.Components hm.Components.getall() Empty or duplicate part containers
Properties hm.Properties hm.Properties.getall() Unreferenced property cards
Materials hm.Materials hm.Materials.getall() Materials with no property reference
Elements hm.Elements hm.Elements.getall() Count per component, orphan elements
Nodes hm.Nodes hm.Nodes.getall() Free nodes not attached to elements
Loads hm.Loads hm.Loads.getall() Stale load collectors from prior runs
Load steps hm.LoadSteps hm.LoadSteps.getall() Empty or duplicated steps
Sets hm.Sets hm.Sets.getall() Orphaned entity sets

Two practical notes on the API. First, getall() returns a collection object, not a Python list; call len() on it for a count and iterate it directly for names. Second, entity names are not guaranteed unique and may be empty strings, so the summary must key on entity ID where a stable identifier is needed and treat the name as descriptive metadata only.

The enumeration script

The script below is self-contained: it imports the HyperMesh API, defines a registry of entity types, walks each one, and prints a classification summary. It runs inside the HyperMesh Python console or as a script executed from the HyperMesh command line, and it depends on nothing outside the standard HyperMesh installation.

import hm

# Registry: (label, module, collector query)
ENTITY_TYPES = [
    ("components", hm.Components, hm.Components.getall),
    ("properties", hm.Properties, hm.Properties.getall),
    ("materials",  hm.Materials,  hm.Materials.getall),
    ("elements",   hm.Elements,   hm.Elements.getall),
    ("nodes",      hm.Nodes,      hm.Nodes.getall),
    ("loads",      hm.Loads,      hm.Loads.getall),
    ("loadsteps",  hm.LoadSteps,  hm.LoadSteps.getall),
    ("sets",       hm.Sets,       hm.Sets.getall),
]

def classify_entities():
    """Enumerate every entity type and return a classification summary."""
    summary = {}
    for label, module, query in ENTITY_TYPES:
        try:
            collection = query()
        except Exception as exc:
            summary[label] = {"count": 0, "error": str(exc), "names": []}
            continue

        names = []
        for entity in collection:
            try:
                names.append(entity.name or "")
            except Exception:
                names.append("")

        summary[label] = {
            "count": len(collection),
            "error": None,
            "names": names,
        }
    return summary

def print_summary(summary):
    print("=" * 56)
    print("HyperMesh entity classification summary")
    print("=" * 56)
    total = 0
    for label, info in summary.items():
        total += info["count"]
        status = "OK" if info["error"] is None else "ERROR: " + info["error"]
        print("{:<12} {:>8}  {}".format(label, info["count"], status))
    print("-" * 56)
    print("{:<12} {:>8}".format("TOTAL", total))
    print("=" * 56)

if __name__ == "__main__":
    result = classify_entities()
    print_summary(result)

Run it from the HyperMesh Python console with exec(open("classify_entities.py").read()), or load the file through the script runner. The output is a fixed-width table with one row per entity type, a count column, and a status column that reports any query failure without aborting the whole run. The try/except around each query is intentional: a model with no load steps, for example, may raise rather than return an empty collection depending on the API version, and you want the remaining types to still be counted.

Reading the summary as a cleanup plan

The counts alone are not a cleanup decision, but they tell you where to look. A model with 240 components and 12 properties is almost certainly carrying duplicate or empty part containers. A model with 0 loads but 6 load steps has stale steps to remove. A model where element count is far below node count suggests free nodes left behind by a failed import. The SVG below shows how the raw counts map onto the three cleanup signals the script is designed to surface.

From entity counts to cleanup signals Raw counts (getall) components 240 properties 12 materials 9 elements 18420 nodes 19204 loads 0 loadsteps 6 sets 3 Signal 1 — container bloat components (240) ≫ properties (12): duplicate or empty part containers likely. Inspect per-component element counts. Action: list components with 0 elements before deletion. Signal 2 — orphan nodes nodes (19204) > elements (18420): ~784 free nodes not attached to any element. Typical after a failed import. Action: query free nodes, confirm, then delete. Signal 3 — stale load steps loads (0) with loadsteps (6): steps reference no load collectors. Left over from prior solver runs. Action: enumerate step contents, remove empty steps.

The three signals are not exhaustive, but they cover the most common cleanup entry points and they are all derivable from the counts the script already produces. The point of the classification summary is to turn a vague instruction like "clean up the model" into a ranked list of specific queries to run next.

Verification and failure modes

To confirm the script is reading the live model rather than a cached state, create a throwaway component in the HyperMesh GUI, re-run the script, and check that the component count increments by exactly one. Delete it and confirm the count returns. If the count does not change, the script is holding a stale collection reference — re-query inside the loop rather than caching the collection at module load.

Two failure modes are worth anticipating. If a query raises because the corresponding collector type is absent from the model, the try/except records the error and continues; check the status column rather than assuming a zero count means "none present." If entity names contain non-ASCII characters, the console may raise an encoding error on print; write the summary to a UTF-8 file instead of stdout when working with imported models from non-English CAD sources.

Save the printed summary to a timestamped text file before and after each cleanup pass. Diffing the two files is the fastest way to confirm that a deletion script removed exactly what you intended and nothing else.
Do not use entity names as unique keys in the summary. HyperMesh permits duplicate and empty names, so two distinct components can both be named "part" or carry no name at all. Key any downstream logic on entity ID, and treat the name column as descriptive only.

Reusable output for the cleanup plan

The script returns a dictionary keyed by entity type, each entry holding a count, an error field, and a list of names. That structure is the contract the rest of the cleanup toolchain can rely on: later scripts can import classify_entities(), compare counts against thresholds, and decide which entity types to hand to a deletion routine. Keeping the enumeration read-only and the output structured means the census can be re-run at any point in the cleanup pipeline without side effects, which is exactly what you want from the first stage of an automation workflow.