1. Inspect HyperMesh Python API and Session State

Why a Session Baseline Comes First

Every cleanup script you write for HyperMesh will make assumptions: which model is active, which entity types exist, which Python modules are importable, and whether the session is running in a GUI or headless context. If those assumptions are wrong, the script fails in confusing ways — a query returns an empty list because the model was never loaded, or an import raises ModuleNotFoundError because the API path differs from what you expected. The purpose of this first case is to replace assumptions with observed facts. You will write a small, self-contained inspection script that queries the HyperMesh Python API, reports the current model and session state, and prints a baseline you can compare against in every later case.

The baseline is not a one-time curiosity. When a cleanup script behaves unexpectedly, the fastest diagnostic is usually to re-run the inspection and compare the current session against the baseline: did the model change, did the entity counts change, did the API version change after an upgrade? Establishing this habit early saves hours of debugging later.

What the HyperMesh Python API Exposes

HyperMesh exposes its Python interface through the hm module (the HyperMesh Python API), with entity access typically organized under hm.entities and model-level operations under hm.Model. The exact surface depends on your HyperMesh version, so the inspection script should not hard-code assumptions about which attributes exist — it should probe them defensively and report what it finds. The key objects to query are:

  • Session context — whether a model is open, the model name, and the working directory.
  • API/library availability — which modules import successfully and their reported versions.
  • Entity inventory — counts of components, properties, materials, elements, and nodes, which form the numeric baseline.
  • Environment — Python version and interpreter path, so results are reproducible across machines.

The SVG below shows how the inspection script fans out from a single entry point to these four query groups and consolidates them into one baseline report. Note that the entity inventory is deliberately shallow here — counting only — because deep enumeration and classification is the job of the next case.

Inspection Script: Query Fan-Out and Baseline Consolidation inspect_session.py single entry point Session Context model open? model name working directory GUI vs headless Library / API import hm hm.entities present? reported version import errors Entity Inventory components properties / materials elements / nodes counts only Environment Python version interpreter path platform timestamp Consolidated Baseline Report structured dict + human-readable printout baseline.json / console output

Writing the Inspection Script

The script below is complete and runnable inside a HyperMesh Python session (or via the HyperMesh command-line Python interpreter). It is written defensively: every API access is wrapped so that a missing attribute or a failed import is reported rather than crashing the whole inspection. This matters because the script must work across HyperMesh versions where the API surface differs.

import sys
import os
import platform
import json
from datetime import datetime

def safe(fn, default=None):
    """Run fn, return default on any exception, and record the error."""
    try:
        return fn()
    except Exception as exc:
        return {"__error__": f"{type(exc).__name__}: {exc}"}

def inspect_library():
    info = {"import_ok": False, "has_entities": False, "version": None}
    try:
        import hm
        info["import_ok"] = True
        info["has_entities"] = hasattr(hm, "entities")
        info["version"] = safe(lambda: getattr(hm, "__version__", None))
    except Exception as exc:
        info["import_error"] = f"{type(exc).__name__}: {exc}"
    return info

def inspect_session():
    import hm
    session = {}
    session["model_open"] = safe(lambda: hm.Model.IsOpen() if hasattr(hm.Model, "IsOpen") else None)
    session["model_name"] = safe(lambda: hm.Model.GetName() if hasattr(hm.Model, "GetName") else None)
    session["cwd"] = os.getcwd()
    session["gui"] = safe(lambda: hm.Info.IsGraphical() if hasattr(hm.Info, "IsGraphical") else None)
    return session

def inspect_entities():
    import hm
    counts = {}
    # Probe common entity collections; report only what exists.
    probes = {
        "components": "Component",
        "properties": "Property",
        "materials": "Material",
        "elements": "Element",
        "nodes": "Node",
    }
    for label, attr in probes.items():
        def _count(attr=attr):
            coll = getattr(hm.entities, attr, None)
            if coll is None:
                return None
            return len(coll)
        counts[label] = safe(_count)
    return counts

def build_baseline():
    return {
        "timestamp": datetime.now().isoformat(timespec="seconds"),
        "python": {
            "version": sys.version.split()[0],
            "executable": sys.executable,
            "platform": platform.platform(),
        },
        "library": inspect_library(),
        "session": inspect_session(),
        "entities": inspect_entities(),
    }

if __name__ == "__main__":
    baseline = build_baseline()
    print(json.dumps(baseline, indent=2, default=str))
    with open("baseline.json", "w") as fh:
        json.dump(baseline, fh, indent=2, default=str)
    print("\nBaseline written to baseline.json")

Reading the Output and Judging the Baseline

Run the script and inspect the JSON. A healthy baseline for a loaded model looks roughly like this (values are illustrative — your counts will differ):

{
  "timestamp": "2025-01-15T10:42:07",
  "python": {
    "version": "3.9.13",
    "executable": "C:/Program Files/Altair/2023/hwdesktop/altair/scripts/python/python.exe",
    "platform": "Windows-10-10.0.19045-SP0"
  },
  "library": {
    "import_ok": true,
    "has_entities": true,
    "version": "2023.1"
  },
  "session": {
    "model_open": true,
    "model_name": "bracket_assembly.hm",
    "cwd": "C:/work/cleanup",
    "gui": true
  },
  "entities": {
    "components": 42,
    "properties": 18,
    "materials": 7,
    "elements": 152340,
    "nodes": 168902
  }
}

Use the following checks to decide whether the baseline is trustworthy before you build anything on top of it:

FieldExpectedIf it looks wrong
library.import_oktrueHyperMesh Python not on sys.path; run inside the HyperMesh interpreter, not system Python.
library.has_entitiestrueAPI version mismatch; check the hm module layout for your release.
session.model_opentrueNo model loaded; open a .hm file before running cleanup scripts.
entities.elementsnon-zero integerModel loaded but empty, or entity attribute name differs in your version.
python.executablepath under HyperMesh installYou are running system Python; the hm import will fail.
Save baseline.json alongside each cleanup project. When a later script produces unexpected counts, diff the current inspection output against the saved baseline to see exactly what changed — model, API version, or environment.
Do not treat None as zero. If entities.components is null, the attribute was not found — that is a different problem from a model with zero components. The safe() wrapper preserves this distinction by returning None for missing attributes and an error dict for exceptions.

Common Failure Symptoms

  • ModuleNotFoundError: No module named 'hm' — the script is running under system Python. Launch it from the HyperMesh Python console or the bundled interpreter shown in python.executable.
  • All entity counts are null — hm.entities exists but the attribute names differ. Print dir(hm.entities) to discover the actual collection names for your version.
  • model_open is false but counts are non-zero — a stale model reference; reload the model and re-run.
  • Inconsistent counts between runs — the model was modified between runs; always capture the baseline immediately before the cleanup operation you intend to verify.

With a verified baseline in hand, every subsequent cleanup script has a reference point: you know the model is loaded, the API is reachable, and the entity counts you expect to change. That is the foundation the rest of this course builds on.