1. Crash Model Assembly Baseline Inventory
Why a Baseline Inventory Comes First
A full-vehicle crash model is never a single file. It is a deck that pulls together a body-in-white (BIW) model, chassis and suspension subsystems, powertrain mounts, restraint systems (airbags, seat belts, pretensioners), dummy/occupant models, barriers and impactors, plus a large set of INCLUDE files for materials, contacts, control cards, and coordinate definitions. Before anyone runs a single explicit time step, the assembly engineer must be able to answer one question with evidence: does every file this deck references actually exist, and does each one carry the revision that the release manifest says it should?
This case builds that evidence. The deliverable is a machine-checkable inventory: a table of every referenced artifact, its resolved location, its declared version tag, and a pass/fail verdict against the release manifest. The task is deliberately scoped to existence and version-tag matching. Path-resolution correctness (relative vs. absolute, ambiguous references) and revision locking are separate concerns handled elsewhere; here we only need to prove that the referenced set is complete and consistent with the manifest.
The Three Sources of Truth
An inventory is only as good as the inputs it reconciles. Three artifacts must be brought together:
- The assembly deck — the top-level input file (for example
full_vehicle_crash_main.key) that contains the*INCLUDEstatements pulling in every subsystem and support file. This defines what is referenced. - The release manifest — a structured list (CSV or YAML) that declares, for each artifact, the expected version tag and owning group. This defines what is expected.
- The build directory tree — the actual files on disk under the assembly root. This defines what exists.
The inventory is the reconciliation of these three. A file that is referenced but absent is a missing artifact. A file that exists but whose version tag differs from the manifest is a version mismatch. A manifest entry that no deck statement references is an orphan and usually signals a stale manifest or a dropped subsystem.
Inventory Data Flow
The diagram below shows how the three sources are parsed and joined into a single verdict table. Note that the join key is the artifact identifier (subsystem name plus relative path), and that the version comparison is a strict string match against the manifest tag — no "close enough" matching is permitted at baseline stage.
Building the Inventory with a Script
Manual reconciliation does not scale to a deck with hundreds of includes. The following Python script parses the deck for *INCLUDE statements, loads the manifest, walks the build tree, and emits the verdict table. It is self-contained and runnable against the sample inputs shown after it.
import csv, os, re, sys
DECK = "full_vehicle_crash_main.key"
MANIFEST = "release_manifest.csv"
ROOT = "assembly_root"
# 1. Parse *INCLUDE statements. LS-DYNA style: *INCLUDE_PATH or *INCLUDE with a filename.
inc_re = re.compile(r'^\*INCLUDE(?:_PATH)?\s*\n?\s*([^\s]+)', re.IGNORECASE)
def parse_deck(path):
refs = []
with open(path, encoding="utf-8") as f:
lines = f.readlines()
for i, line in enumerate(lines):
m = inc_re.match(line.strip())
if m:
refs.append(m.group(1).strip('"\''))
# handle *INCLUDE on its own line followed by filename on next line
elif line.strip().upper() == "*INCLUDE" and i + 1 < len(lines):
refs.append(lines[i + 1].strip().strip('"\''))
return refs
# 2. Load manifest: artifact_id -> version_tag
def load_manifest(path):
manifest = {}
with open(path, newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
manifest[row["artifact_id"].strip()] = row["version_tag"].strip()
return manifest
# 3. Walk disk: relative path -> (exists, version_tag from filename or sidecar)
def scan_disk(root):
found = {}
for dirpath, _, files in os.walk(root):
for fn in files:
rel = os.path.relpath(os.path.join(dirpath, fn), root).replace("\\", "/")
found[rel] = fn
return found
def artifact_id_from_path(p):
# strip directory and extension: body/body_biw_v2.4.1.key -> body_biw
base = os.path.basename(p)
base = re.sub(r'_v\d+(\.\d+)*', '', base)
return os.path.splitext(base)[0]
def tag_from_path(p):
m = re.search(r'_(v\d+(?:\.\d+)*)', os.path.basename(p))
return m.group(1) if m else ""
def main():
refs = parse_deck(DECK)
manifest = load_manifest(MANIFEST)
disk = scan_disk(ROOT)
rows = []
referenced_ids = set()
for ref in refs:
aid = artifact_id_from_path(ref)
referenced_ids.add(aid)
exists = ref in disk
deck_tag = tag_from_path(ref)
man_tag = manifest.get(aid, "")
if not exists:
verdict = "MISSING"
elif man_tag and deck_tag != man_tag:
verdict = "MISMATCH"
elif not man_tag:
verdict = "UNMANIFESTED"
else:
verdict = "PASS"
rows.append((aid, ref, "yes" if exists else "no", deck_tag, man_tag, verdict))
# orphans: manifest entries never referenced
for aid, tag in manifest.items():
if aid not in referenced_ids:
rows.append((aid, "-", "-", "-", tag, "ORPHAN"))
with open("inventory_report.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["artifact_id", "resolved_path", "exists", "deck_tag", "manifest_tag", "verdict"])
w.writerows(rows)
fails = [r for r in rows if r[5] != "PASS"]
print(f"{len(rows)} artifacts checked, {len(fails)} non-PASS")
for r in fails:
print(" ", r[5], r[0], r[1])
sys.exit(1 if fails else 0)
if __name__ == "__main__":
main()
The script exits non-zero when any artifact is not PASS, which makes it usable as a pre-assembly gate in CI. The sample inputs below are illustrative teaching data, not real project files.
# release_manifest.csv
artifact_id,version_tag,owner
body_biw,v2.4.1,body
chassis_front,v1.9.0,chassis
restraints_dab,v3.1.0,restraints
powertrain_mount,v1.2.3,powertrain
# assembly_root/ (illustrative tree)
body/body_biw_v2.4.1.key
restraints/restraints_dab_v3.1.2.key
powertrain/powertrain_mount_v1.2.3.key
# note: chassis/chassis_front_v1.9.0.key is absent on purpose
Running against these inputs produces four non-PASS rows: chassis_front as MISSING, restraints_dab as MISMATCH (deck v3.1.2 vs manifest v3.1.0), barrier_offset as ORPHAN if it appears in the manifest but not the deck, and any deck reference with no manifest entry as UNMANIFESTED. These are expected outcomes of the given data, not measured results.
Reading the Verdicts
| Verdict | Meaning | Typical cause | Action before assembly |
|---|---|---|---|
| PASS | File exists and tag matches manifest | — | None |
| MISSING | Referenced but not on disk | File not delivered, wrong root, typo in path | Obtain correct revision or fix reference |
| MISMATCH | Exists but tag ≠ manifest | Stale copy, wrong branch checked out | Replace with manifest revision |
| UNMANIFESTED | Referenced, no manifest entry | New include not registered | Add to manifest or remove reference |
| ORPHAN | In manifest, never referenced | Dropped subsystem, stale manifest | Confirm intent; clean manifest |
PASS here means only that the file exists and its tag matches. It does not certify that the path resolves correctly at solve time, nor that the revision is locked. Those checks belong to the path-resolution audit and version-locking stages and must not be assumed complete from this inventory alone.Verification and Failure Symptoms
To confirm the inventory is trustworthy, cross-check the report against the deck: the count of *INCLUDE statements should equal the number of non-orphan rows. If the script reports fewer references than the deck visibly contains, the parser is missing a syntax variant (for example *INCLUDE_PATH with a trailing offset field, or a filename on the following line). If every row is MISSING, the ROOT constant points at the wrong directory. If tags are empty across the board, filenames do not follow the _vX.Y.Z convention and the tag must be read from a sidecar or header instead. Resolve these parser-level issues before trusting any verdict, because a silently under-parsed deck will report a clean inventory that is actually incomplete.