36 KiB
Impakt
Crash test data analysis, visualization, and reporting.
Impakt is a modular, scriptable Python toolkit for working with automotive crash test data. It reads ISO 13499 MME data natively, provides non-destructive signal processing, interactive web-based visualization, injury criteria calculation, and protocol-compliant report generation for Euro NCAP, US NCAP, and IIHS.
Quick Start
# Install
uv sync --dev
# Run tests (136 tests)
uv run pytest tests/
# View test metadata
uv run impakt info tests/mme_data/3239
# Launch web UI
uv run impakt serve tests/mme_data/3239
# Python scripting
uv run python -c "
from impakt import Session
s = Session.open('tests/mme_data/3239')
print(s) # Session(3239, 133 channels)
"
See docs/STATUS.md for detailed project state, known issues, and next steps.
Contents
- #Design Principles
- #Architecture Overview
- #Module Breakdown
- #impakt.io — Data IO
- #impakt.channel — Channel Model
- #impakt.transform — Signal Processing
- #impakt.criteria — Injury Criteria
- #impakt.protocol — Rating Protocols
- #impakt.plot — Visualization
- #impakt.report — PDF and Report Generation
- #impakt.template — Templates and Sessions
- #impakt.web — Web UI
- #impakt.plugin — Plugin System
- #impakt.script — Scripting API
- #Data Flow
- #Template and Session Lifecycle
- #Injury Criteria Pipeline
- #Web UI Architecture
- #Plugin Architecture
- #ISO Channel Naming Intelligence
- #Directory Structure
- #Scripting Examples
Design Principles
- Immutable raw data. Original MME files are never modified. All state — sessions, cached computations, user overrides — lives in a
.impakt/subfolder alongside the test data. - Non-destructive transforms. Filtering, alignment, and math operations produce new channel views. The transformation chain is stored and reproducible.
- Scriptable first. Every operation available in the UI is accessible through a Python API. The web UI is a frontend to the same engine.
- Template-driven workflow. Reusable templates define plot layouts, filter chains, channel selections, corridors, and report configurations. Templates live in a global library; sessions bind templates to specific test data.
- Protocol-aware. Built-in knowledge of Euro NCAP, US NCAP, and IIHS injury criteria, scoring thresholds, and report formats.
- Plugin-extensible. Custom readers, transforms, injury criteria, report templates, and UI components can be registered through a plugin API.
Architecture Overview
graph TB
subgraph "Data Layer"
IO[impakt.io]
CH[impakt.channel]
end
subgraph "Processing Layer"
TR[impakt.transform]
CR[impakt.criteria]
PR[impakt.protocol]
end
subgraph "Presentation Layer"
PL[impakt.plot]
RP[impakt.report]
WB[impakt.web]
end
subgraph "Orchestration Layer"
TM[impakt.template]
SC[impakt.script]
PG[impakt.plugin]
end
IO -->|"parse MME/other"| CH
CH -->|"channel data"| TR
TR -->|"filtered/aligned"| CR
CR -->|"injury values"| PR
PR -->|"scores & ratings"| RP
CH -->|"raw & derived"| PL
TR -->|"processed"| PL
CR -->|"criteria results"| PL
PL -->|"figures"| RP
PL -->|"interactive plots"| WB
TM -->|"orchestrates"| IO
TM -->|"orchestrates"| TR
TM -->|"orchestrates"| PL
TM -->|"orchestrates"| CR
SC -->|"drives"| TM
SC -->|"drives"| IO
SC -->|"drives"| TR
SC -->|"drives"| PL
PG -.->|"extends"| IO
PG -.->|"extends"| TR
PG -.->|"extends"| CR
PG -.->|"extends"| PR
PG -.->|"extends"| RP
WB -->|"calls"| SC
Module Breakdown
impakt.io — Data IO
Responsible for reading crash test data from disk into the internal channel model.
Primary format: ISO 13499 MME (directory-based: MME.ini + .chn/.dat file pairs).
Architecture: Reader classes implement a common Reader protocol, enabling future format support (TDMS, CSV, UDB) without changing downstream code.
classDiagram
class ReaderProtocol {
<<protocol>>
+read(path: Path) TestData
+supports(path: Path) bool
+metadata(path: Path) TestMetadata
}
class MMEReader {
+read(path: Path) TestData
+supports(path: Path) bool
+metadata(path: Path) TestMetadata
-_parse_ini(path: Path) dict
-_parse_chn(path: Path) ChannelHeader
-_read_dat(path: Path, header: ChannelHeader) ndarray
}
class TDMSReader {
+read(path: Path) TestData
+supports(path: Path) bool
+metadata(path: Path) TestMetadata
}
class CSVReader {
+read(path: Path) TestData
+supports(path: Path) bool
+metadata(path: Path) TestMetadata
}
class ReaderRegistry {
+register(reader: ReaderProtocol)
+detect(path: Path) ReaderProtocol
+read(path: Path) TestData
}
ReaderProtocol <|.. MMEReader
ReaderProtocol <|.. TDMSReader
ReaderProtocol <|.. CSVReader
ReaderRegistry o-- ReaderProtocol
Key responsibilities:
- Parse
MME.inifor test-level metadata (vehicle, dummy, impact config) - Parse
.chnheaders for per-channel metadata (units, sample rate, CFC class, pre-trigger) - Read
.datfiles (ASCII or binary IEEE float) into NumPy arrays - Reconstruct time vectors from
dtand pre-trigger sample count - Populate
TestDataandChannelobjects
impakt.channel — Channel Model
The core data model. Channels are immutable value objects wrapping time-series data with rich metadata.
classDiagram
class TestData {
+test_id: str
+metadata: TestMetadata
+channels: dict~str, Channel~
+path: Path
+get(name: str) Channel
+find(pattern: str) list~Channel~
+groups() dict~str, ChannelGroup~
}
class TestMetadata {
+test_number: str
+test_date: date
+test_type: str
+vehicle: VehicleInfo
+dummy: DummyInfo
+impact: ImpactConfig
}
class Channel {
+name: str
+code: ChannelCode
+data: ndarray
+time: ndarray
+unit: str
+sample_rate: float
+cfc_class: int | None
+metadata: dict
}
class ChannelCode {
+raw: str
+test_object: str
+main_location: str
+fine_location: str
+measurement: str
+direction: str
+sense: str
+filter_class: str
+group_key() str
+is_component() bool
+axis() str
}
class ChannelGroup {
+key: str
+x: Channel | None
+y: Channel | None
+z: Channel | None
+resultant() Channel
+components() list~Channel~
}
TestData *-- TestMetadata
TestData *-- Channel
Channel *-- ChannelCode
ChannelGroup o-- Channel
ISO channel naming intelligence is embedded in ChannelCode. Given a raw 16-character name like 11HEAD0000H3ACXA, the parser extracts:
| Field | Positions | Example | Meaning |
|---|---|---|---|
| Test Object | 1-2 | 11 |
Driver, Hybrid III |
| Main Location | 3-6 | HEAD |
Head |
| Fine Location | 7-10 | 0000 |
Center of gravity |
| Measurement | 11-12 | AC |
Acceleration |
| Direction | 13 | X |
Longitudinal axis |
| Sense | 14 | A |
SAE sign convention A |
Auto-grouping: Channels sharing positions 1-12 + 14-16 (differing only at position 13: X/Y/Z) are automatically grouped into ChannelGroup objects for one-call resultant computation.
impakt.transform — Signal Processing
Non-destructive signal processing pipeline. Each transform takes a Channel and returns a new Channel — the original is never mutated.
graph LR
subgraph "Transform Pipeline"
RAW[Raw Channel] --> F1[CFC Filter]
F1 --> F2[X-Axis Align]
F2 --> F3[Y-Axis Zero]
F3 --> F4[Math Expression]
F4 --> OUT[Derived Channel]
end
style RAW fill:#e1f5fe
style OUT fill:#c8e6c9
Available transforms:
| Transform | Description | Parameters |
|---|---|---|
CFCFilter |
SAE J211 CFC filtering (4th-order Butterworth, zero-phase) | cfc_class: 60, 180, 600, 1000 |
XAlign |
Time-zero shifting — moves t=0 to a user-specified event | method: manual, threshold, trigger |
YAlign |
Zero-offset correction using average-over-time window | window: (t_start, t_end) for baseline |
Resultant |
Compute vector magnitude from X/Y/Z components | group: ChannelGroup |
MathExpr |
Free-form math on channels (e.g., ch_a + ch_b * 0.5) |
expression: str, channels: dict |
Trim |
Extract time range | t_start, t_end |
Resample |
Change sample rate | target_rate: float |
Transform chain model:
classDiagram
class Transform {
<<protocol>>
+apply(channel: Channel) Channel
+name: str
+params: dict
}
class TransformChain {
+steps: list~Transform~
+apply(channel: Channel) Channel
+append(transform: Transform) TransformChain
+serialize() dict
+deserialize(data: dict) TransformChain
}
class CFCFilter {
+cfc_class: int
+apply(channel: Channel) Channel
}
class XAlign {
+method: str
+reference_time: float
+apply(channel: Channel) Channel
}
class YAlign {
+window: tuple~float, float~
+apply(channel: Channel) Channel
}
class Resultant {
+apply(group: ChannelGroup) Channel
}
class MathExpr {
+expression: str
+apply(channels: dict) Channel
}
Transform <|.. CFCFilter
Transform <|.. XAlign
Transform <|.. YAlign
Transform <|.. Resultant
Transform <|.. MathExpr
TransformChain o-- Transform
CFC filter implementation (SAE J211 compliant):
- 4th-order Butterworth low-pass, applied forward-reverse via
scipy.signal.filtfilt - Cutoff frequency:
f_c = CFC_class * (5/3)Hz - CFC 60 = 100 Hz, CFC 180 = 300 Hz, CFC 600 = 1000 Hz, CFC 1000 = 1650 Hz
impakt.criteria — Injury Criteria
Calculation engine for standard injury criteria. Each criterion is a self-contained function operating on filtered channel data.
classDiagram
class InjuryCriterion {
<<protocol>>
+name: str
+required_channels: list~str~
+compute(channels: dict~str, Channel~, dummy: DummyInfo) CriterionResult
}
class CriterionResult {
+criterion: str
+value: float
+unit: str
+time_of_peak: float | None
+window: tuple~float, float~ | None
+details: dict
}
class HIC {
+window_ms: int
+compute(...) CriterionResult
}
class Clip3ms {
+compute(...) CriterionResult
}
class Nij {
+intercepts: NijIntercepts
+compute(...) CriterionResult
}
class ChestDeflection {
+compute(...) CriterionResult
}
class FemurLoad {
+compute(...) CriterionResult
}
class TibiaIndex {
+compute(...) CriterionResult
}
class ViscousCriterion {
+compute(...) CriterionResult
}
InjuryCriterion <|.. HIC
InjuryCriterion <|.. Clip3ms
InjuryCriterion <|.. Nij
InjuryCriterion <|.. ChestDeflection
InjuryCriterion <|.. FemurLoad
InjuryCriterion <|.. TibiaIndex
InjuryCriterion <|.. ViscousCriterion
InjuryCriterion --> CriterionResult
Implemented criteria:
| Criterion | Formula / Method | Key Inputs | Reference |
|---|---|---|---|
| HIC15 / HIC36 | (t2-t1) * [avg(a)]^2.5, maximized over window |
Head resultant accel (g) | FMVSS 208 |
| 3ms Clip | Max accel sustained for cumulative 3 ms | Chest resultant accel (g) | SAE J211 |
| Nij | Fz/Fzc + My/Myc, max of 4 modes |
Upper neck Fz, My | FMVSS 208 |
| Chest Deflection | Peak sternal displacement | Chest deflection (mm) | FMVSS 208 |
| Femur Load | Peak compressive axial force | Femur Fz left/right (kN) | FMVSS 208 |
| Tibia Index | |M|/Mc + |F|/Fc |
Tibia Mx, My, Fz | Euro NCAP |
| Viscous Criterion | V(t) * C(t) (velocity x compression) |
Chest deflection time-history | Euro NCAP |
Nij critical intercepts by dummy type:
| Dummy | Fzc Tension (N) | Fzc Compression (N) | Myc Flexion (Nm) | Myc Extension (Nm) |
|---|---|---|---|---|
| Hybrid III 50th M | 6806 | 6160 | 310 | 135 |
| Hybrid III 5th F | 4287 | 3880 | 155 | 67 |
| Hybrid III 6YO | 2800 | 2800 | 93 | 37 |
| Hybrid III 3YO | 2120 | 2120 | 68 | 27 |
impakt.protocol — Rating Protocols
Maps computed injury criteria to protocol-specific scores and ratings.
graph TB
subgraph "Criteria Results"
HIC[HIC15: 423]
CHEST[Chest Defl: 34mm]
FEMUR[Femur: 4.2kN]
NIJ[Nij: 0.61]
TI[Tibia Index: 0.8]
end
subgraph "Protocol Engines"
ENCAP[Euro NCAP Scorer]
USNCAP[US NCAP Scorer]
IIHS_E[IIHS Evaluator]
end
subgraph "Outputs"
ENCAP_R["Euro NCAP\n4 stars\nAdult: 82%"]
USNCAP_R["US NCAP\n5 stars\nP(injury): 8%"]
IIHS_R["IIHS\nGood\nAll sub-ratings: G"]
end
HIC --> ENCAP & USNCAP & IIHS_E
CHEST --> ENCAP & USNCAP & IIHS_E
FEMUR --> ENCAP & USNCAP & IIHS_E
NIJ --> ENCAP & USNCAP & IIHS_E
TI --> ENCAP
ENCAP --> ENCAP_R
USNCAP --> USNCAP_R
IIHS_E --> IIHS_R
Protocol scoring:
| Protocol | Methodology | Output |
|---|---|---|
| Euro NCAP | Sliding-scale performance limits per body region, mapped to color codes (Green / Yellow / Orange / Brown / Red) and points. Area percentages determine star rating. | Stars (0-5), body region colors, area scores |
| US NCAP | Injury risk functions convert criteria to probability of AIS 3+ injury. Combined probability maps to stars. | Stars (1-5), injury probability |
| IIHS | Threshold-based per criterion: Good / Acceptable / Marginal / Poor. Overall = worst sub-rating with adjustment. | G/A/M/P per region, overall rating |
Protocol thresholds are versioned and configurable — scoring rules change every few years and Impakt stores these as versioned protocol definition files.
impakt.plot — Visualization
Plotting engine built on Plotly for interactive web-based visualization.
Capabilities:
- Single channel time-history plots
- Multi-channel overlay (same test, different channels)
- Multi-test overlay (same channel, different tests)
- Dual X-axis cursors with value readout for all plotted channels
- Tolerance corridors (user-defined or from templates)
- Zoom, pan, box select
- Resultant plots auto-computed from grouped components
classDiagram
class PlotSpec {
+channels: list~ChannelRef~
+corridors: list~Corridor~
+x_cursors: tuple~float, float~ | None
+x_range: tuple~float, float~ | None
+y_range: tuple~float, float~ | None
+title: str
+x_label: str
+y_label: str
}
class ChannelRef {
+test_id: str
+channel_name: str
+transform_chain: TransformChain | None
+style: PlotStyle
}
class Corridor {
+name: str
+upper: ndarray
+lower: ndarray
+time: ndarray
+style: CorridorStyle
}
class PlotStyle {
+color: str
+line_width: float
+line_dash: str
+label: str
}
class PlotEngine {
+render(spec: PlotSpec) PlotlyFigure
+to_image(spec: PlotSpec, format: str) bytes
+to_html(spec: PlotSpec) str
}
PlotSpec *-- ChannelRef
PlotSpec *-- Corridor
ChannelRef *-- PlotStyle
PlotEngine --> PlotSpec
Dual X-axis cursor — the user selects two time points (click or explicit entry). A table displays the interpolated value of every plotted channel at both cursor positions, plus the delta.
impakt.report — PDF and Report Generation
Generates single-page-per-plot PDFs and multi-section protocol reports.
graph LR
PLOTS[Plot Specs] --> RE[Report Engine]
CRITERIA[Criteria Results] --> RE
PROTOCOL[Protocol Scores] --> RE
META[Test Metadata] --> RE
TMPL[Report Template] --> RE
RE --> PDF[PDF Output]
RE --> HTML[HTML Output]
Report types:
- Plot sheets — one plot per page, with metadata header (test ID, channel info, filter state)
- Injury summary — tabular criteria results with color-coded pass/fail per protocol
- Full protocol report — Euro NCAP / US NCAP / IIHS formatted report with all required sections, body region diagrams, and scoring breakdowns
impakt.template — Templates and Sessions
The template/session system enables reusable analysis workflows and per-test state persistence.
| Concept | Template | Session |
|---|---|---|
| Lives in | Global library (~/.impakt/templates/) |
Alongside test data (.impakt/ subfolder) |
| Contains | Plot layouts, filter chains, channel selections, corridors, report configs | Template reference + test-specific overrides + cached results |
| Purpose | Reusable recipe ("show me frontal NCAP analysis") | Specific instance ("test_001 viewed with frontal NCAP, but I moved the cursor") |
| Mutability | Edited in library, versioned | Auto-saved per test, can "promote" to template |
graph TB
subgraph "Global Library ~/.impakt/templates/"
T1[frontal_ncap.yaml]
T2[side_iihs.yaml]
T3[custom_analysis.yaml]
end
subgraph "Test Data Directory"
MME[test_001/MME.ini]
subgraph ".impakt/"
S1[session.yaml]
CACHE[cache/]
end
end
T1 -->|"user opens test\nwith template"| S1
S1 -->|"references"| T1
S1 -->|"stores overrides"| S1
S1 -->|"promote changes"| T1
Template spec (YAML):
name: "Frontal NCAP Analysis"
version: 2
plots:
- title: "Head Acceleration"
channels:
- pattern: "11HEAD0000AC{X,Y,Z}A"
transform:
- type: cfc_filter
cfc_class: 1000
- pattern: "11HEAD0000ACRA"
transform:
- type: resultant
corridors:
- name: "HIC 700 Reference"
file: "corridors/hic_700.csv"
- title: "Chest Deflection"
channels:
- pattern: "11CHST****DC*A"
x_cursors: [0.0, 0.080]
filters:
default_cfc: 180
criteria:
- hic15
- clip_3ms
- nij
- chest_deflection
- femur_load
protocol: euro_ncap_2024
report:
format: pdf
template: euro_ncap_full
Session spec (stored in test_data/.impakt/session.yaml):
template: "frontal_ncap"
template_version: 2
test_path: "/data/tests/test_001"
overrides:
plots:
0:
x_cursors: [0.012, 0.065]
filters:
"11HEAD0000ACXA":
- type: cfc_filter
cfc_class: 600
cached_results:
hic15: 423.7
last_computed: "2026-04-10T14:30:00"
Lifecycle:
sequenceDiagram
participant User
participant Template Library
participant Session
participant Engine
User->>Template Library: Select template
Template Library->>Session: Instantiate session
User->>Engine: Open test data
Engine->>Session: Bind to test data
Engine->>Engine: Apply template (load channels, filters, plots)
User->>Session: Modify (move cursors, change filter)
Session->>Session: Store override
User->>Template Library: (optional) Promote override to template
User->>Session: Close
Session->>Session: Auto-save to .impakt/
impakt.web — Web UI
Interactive web application built with Dash (Plotly).
graph TB
subgraph "Dash Layout"
direction TB
HEADER[Header Bar: Test Info + Template Selector]
subgraph "Main Area"
direction LR
subgraph "Left Panel"
TREE[Channel Tree\nISO-aware hierarchy]
FILTER_PANEL[Transform Controls\nCFC / Align / Math]
end
subgraph "Center"
PLOT_AREA[Plot Area\nMultiple plot panes]
CURSOR_TABLE[Cursor Values Table\nx1 | x2 | delta]
end
subgraph "Right Panel"
CORRIDOR_PANEL[Corridor Manager]
CRITERIA_PANEL[Criteria Results]
end
end
FOOTER[Report Generation + Export Controls]
end
TREE -->|"channel selection\ncallback"| PLOT_AREA
FILTER_PANEL -->|"transform\ncallback"| PLOT_AREA
PLOT_AREA -->|"cursor position\ncallback"| CURSOR_TABLE
CORRIDOR_PANEL -->|"corridor data\ncallback"| PLOT_AREA
CRITERIA_PANEL -->|"annotation\ncallback"| PLOT_AREA
FOOTER -->|"generate\ncallback"| PLOT_AREA
Key UI components:
- Channel tree — hierarchical browser organized by test object > body region > measurement type, leveraging ISO naming intelligence
- Plot pane — Plotly figures with zoom / pan / box-select, corridor overlays
- Cursor table — interpolated values at two user-selected X-axis times for all visible channels, plus delta
- Transform sidebar — apply / remove filters, alignment, math expressions
- Template panel — load, save, promote templates
- Report panel — select protocol, generate PDF
impakt.plugin — Plugin System
Formal plugin architecture for extending every layer.
classDiagram
class PluginRegistry {
+register(plugin: ImpaktPlugin)
+discover(path: Path)
+get_readers() list~ReaderProtocol~
+get_transforms() list~Transform~
+get_criteria() list~InjuryCriterion~
+get_protocols() list~Protocol~
+get_report_templates() list~ReportTemplate~
}
class ImpaktPlugin {
<<protocol>>
+name: str
+version: str
+register(registry: PluginRegistry)
}
class ReaderPlugin {
+reader: ReaderProtocol
}
class TransformPlugin {
+transform: Transform
}
class CriterionPlugin {
+criterion: InjuryCriterion
}
class ProtocolPlugin {
+protocol: Protocol
}
ImpaktPlugin <|.. ReaderPlugin
ImpaktPlugin <|.. TransformPlugin
ImpaktPlugin <|.. CriterionPlugin
ImpaktPlugin <|.. ProtocolPlugin
PluginRegistry o-- ImpaktPlugin
Discovery mechanisms:
- Entry points (
pyproject.toml/setup.cfgentry point groups) - Directory scanning (
~/.impakt/plugins/) - Explicit registration via API
Example plugin integration:
graph TB
subgraph "Core Impakt"
REG[Plugin Registry]
IO_CORE[io: MMEReader]
TR_CORE[transform: CFC, Align, ...]
CR_CORE[criteria: HIC, Nij, ...]
PR_CORE[protocol: ENCAP, USNCAP, IIHS]
end
subgraph "Plugin: impakt-tdms"
TDMS_P[TDMSReader]
end
subgraph "Plugin: impakt-jncap"
JNCAP_P[JNCAP Scorer]
JNCAP_R[JNCAP Report Template]
end
subgraph "Plugin: impakt-custom-filter"
CUSTOM_F[WaveletDenoise Transform]
end
subgraph "Discovery"
EP[Entry Points\npyproject.toml]
DIR[~/.impakt/plugins/]
API_R[Explicit API\nregistry.register]
end
EP --> REG
DIR --> REG
API_R --> REG
REG -->|"extends"| IO_CORE
REG -->|"extends"| TR_CORE
REG -->|"extends"| CR_CORE
REG -->|"extends"| PR_CORE
TDMS_P --> REG
JNCAP_P --> REG
JNCAP_R --> REG
CUSTOM_F --> REG
impakt.script — Scripting API
The top-level API that scripts and the web UI both call.
from impakt import Session, Template
# Load test data
test = Session.open("/data/tests/test_001")
# Browse channels using ISO naming intelligence
head_channels = test.find("11HEAD0000AC*")
# [Channel(11HEAD0000ACXA), Channel(11HEAD0000ACYA), Channel(11HEAD0000ACZA)]
# Auto-detect X/Y/Z group, compute resultant
head_group = test.group("11HEAD0000AC")
head_resultant = head_group.resultant()
# Apply CFC filter (non-destructive)
filtered = head_resultant.transform.cfc(1000)
# Compute HIC15
from impakt.criteria import hic
result = hic(filtered, window_ms=15)
print(f"HIC15 = {result.value:.1f} at t = {result.time_of_peak:.4f}s")
# Apply a template
tmpl = Template.load("frontal_ncap")
session = tmpl.apply(test)
# Generate protocol report
from impakt.protocol import euro_ncap
report = euro_ncap.evaluate(session, version="2024")
report.to_pdf("test_001_encap.pdf")
# Launch web UI
from impakt.web import serve
serve(test, template="frontal_ncap", port=8050)
Data Flow
End-to-end data flow from MME files to PDF reports:
graph TB
MME["MME Directory\n(immutable)"] -->|"impakt.io"| TD[TestData Object]
TD -->|"channel access"| CH[Channel Objects]
CH -->|"impakt.transform"| TCH[Transformed Channels]
TCH -->|"impakt.criteria"| CR[Criterion Results]
CR -->|"impakt.protocol"| SC[Protocol Scores]
CH -->|"impakt.plot"| FIG1[Raw Plots]
TCH -->|"impakt.plot"| FIG2[Filtered Plots]
CR -->|"impakt.plot"| FIG3[Criteria Annotations]
FIG2 --> RP[impakt.report]
SC --> RP
RP --> PDF[PDF Report]
TD -.->|"state saved"| IMPAKT_DIR[".impakt/ subfolder"]
IMPAKT_DIR -.->|"state loaded"| TD
style MME fill:#e1f5fe
style IMPAKT_DIR fill:#fff9c4
style PDF fill:#c8e6c9
Template and Session Lifecycle
stateDiagram-v2
[*] --> TemplateLibrary: User browses templates
TemplateLibrary --> SessionCreated: Select template + open test data
SessionCreated --> Active: Engine applies template
Active --> Active: User modifies\n(cursors, filters, channels)
Active --> Saved: Auto-save to .impakt/
Saved --> Active: Reopen test data
Saved --> TemplateLibrary: Promote overrides\nback to template
Active --> ReportGenerated: User generates report
ReportGenerated --> Active: Continue analysis
Active --> [*]: Close session
Injury Criteria Pipeline
How raw channel data flows through to a protocol rating:
graph LR
subgraph "1. Channel Selection"
A1["HEAD AC X/Y/Z"]
A2["NECK FO X/Z"]
A3["NECK MO Y"]
A4["CHEST DC"]
A5["FEMUR FO Z L/R"]
A6["TIBIA FO + MO"]
end
subgraph "2. Transform"
B1[CFC 1000\n+ Resultant]
B2[CFC 600]
B3[CFC 600]
B4[CFC 180]
B5[CFC 600]
B6[CFC 600]
end
subgraph "3. Criteria"
C1[HIC15 / HIC36]
C2[Nij]
C3[Nij]
C4["Chest Defl\n3ms Clip\nViscous"]
C5[Femur Load]
C6[Tibia Index]
end
subgraph "4. Protocol"
D1["Euro NCAP\nColor + Points"]
D2["US NCAP\nP(injury) + Stars"]
D3["IIHS\nG/A/M/P"]
end
A1 --> B1 --> C1
A2 --> B2 --> C2
A3 --> B3 --> C3
A4 --> B4 --> C4
A5 --> B5 --> C5
A6 --> B6 --> C6
C1 & C2 & C3 & C4 & C5 & C6 --> D1
C1 & C2 & C3 & C4 & C5 --> D2
C1 & C2 & C3 & C4 & C5 & C6 --> D3
Web UI Architecture
graph TB
subgraph "Browser"
UI[Dash Frontend]
PLOTS_V[Interactive Plots]
CURSOR[Dual X-Cursor + Values Table]
TREE[Channel Tree Browser]
TMPL_UI[Template Selector]
REPORT_UI[Report Generator Panel]
end
subgraph "Server"
DASH[Dash Server]
API[Impakt Script API]
CACHE_S[Computation Cache]
end
UI --> DASH
DASH --> API
API --> CACHE_S
TREE -->|"select channels"| PLOTS_V
TMPL_UI -->|"apply template"| API
CURSOR -->|"x1, x2 positions"| PLOTS_V
REPORT_UI -->|"generate"| API
Plugin Architecture
graph LR
subgraph "Extension Points"
R[Readers]
T[Transforms]
C[Criteria]
P[Protocols]
RT[Report Templates]
end
REG[Plugin Registry] --> R & T & C & P & RT
subgraph "Discovery"
EP[pyproject.toml\nentry_points]
DP[~/.impakt/plugins/]
EX[registry.register]
end
EP & DP & EX --> REG
ISO Channel Naming Intelligence
The ChannelCode parser powers auto-discovery, grouping, and human-readable descriptions throughout the tool.
graph LR
RAW["11HEAD0000H3ACXA"] --> PARSER[ChannelCode Parser]
PARSER --> OBJ["Test Object: 11\nDriver, Hybrid III"]
PARSER --> LOC["Location: HEAD\nHead"]
PARSER --> FINE["Fine: 0000\nCenter of Gravity"]
PARSER --> MEAS["Measurement: AC\nAcceleration"]
PARSER --> DIR["Direction: X\nLongitudinal"]
PARSER --> SENSE["Sense: A\nSAE Convention"]
OBJ & LOC & FINE & MEAS & DIR --> GROUP["Group Key:\n11HEAD0000AC_A\n(X/Y/Z family)"]
GROUP --> AUTO["Auto-features:\n- Resultant computation\n- Channel tree placement\n- Criteria channel matching\n- Human-readable label"]
Lookup tables for all code segments are bundled with the package and extensible via plugins:
| Segment | Examples |
|---|---|
| Test objects | 11 = Driver H3 50th, 12 = Passenger, 20 = Barrier, 30 = Pedestrian |
| Main locations | HEAD, NECK, CHST, PELV, FEMR, TIBI, STCL, BPIL |
| Fine locations | 0000 = CG, UP00 = Upper, LO00 = Lower, LE00 = Left, RI00 = Right |
| Measurements | AC = Acceleration, FO = Force, MO = Moment, DS = Displacement, DC = Deflection |
| Directions | X = Longitudinal, Y = Lateral, Z = Vertical, R = Resultant |
Directory Structure
impakt/
├── pyproject.toml
├── README.md
├── src/
│ └── impakt/
│ ├── __init__.py
│ ├── io/
│ │ ├── __init__.py
│ │ ├── reader.py # ReaderProtocol, ReaderRegistry
│ │ ├── mme.py # MMEReader
│ │ ├── tdms.py # TDMSReader (stub)
│ │ └── csv.py # CSVReader (stub)
│ ├── channel/
│ │ ├── __init__.py
│ │ ├── model.py # Channel, TestData, TestMetadata
│ │ ├── code.py # ChannelCode parser
│ │ ├── group.py # ChannelGroup, auto-grouping
│ │ └── lookup.py # ISO code lookup tables
│ ├── transform/
│ │ ├── __init__.py
│ │ ├── base.py # Transform protocol, TransformChain
│ │ ├── cfc.py # CFCFilter
│ │ ├── align.py # XAlign, YAlign
│ │ ├── resultant.py # Resultant
│ │ ├── math_expr.py # MathExpr
│ │ └── resample.py # Resample, Trim
│ ├── criteria/
│ │ ├── __init__.py
│ │ ├── base.py # InjuryCriterion protocol
│ │ ├── hic.py # HIC15, HIC36
│ │ ├── clip3ms.py # 3ms chest clip
│ │ ├── nij.py # Neck injury criterion
│ │ ├── chest.py # Chest deflection, Viscous criterion
│ │ ├── femur.py # Femur load
│ │ └── tibia.py # Tibia index
│ ├── protocol/
│ │ ├── __init__.py
│ │ ├── base.py # Protocol protocol, version management
│ │ ├── euro_ncap.py # Euro NCAP scorer
│ │ ├── us_ncap.py # US NCAP scorer
│ │ ├── iihs.py # IIHS evaluator
│ │ └── thresholds/ # Versioned threshold YAML files
│ │ ├── euro_ncap_2024.yaml
│ │ ├── us_ncap_2023.yaml
│ │ └── iihs_2024.yaml
│ ├── plot/
│ │ ├── __init__.py
│ │ ├── engine.py # PlotEngine (Plotly)
│ │ ├── spec.py # PlotSpec, ChannelRef, Corridor
│ │ ├── cursor.py # Dual X-cursor logic
│ │ └── export.py # PNG/SVG/PDF single-plot export
│ ├── report/
│ │ ├── __init__.py
│ │ ├── engine.py # ReportEngine
│ │ ├── pdf.py # PDF generation (WeasyPrint)
│ │ └── templates/ # Jinja2 HTML report templates
│ │ ├── plot_sheet.html
│ │ ├── injury_summary.html
│ │ └── protocol_report.html
│ ├── template/
│ │ ├── __init__.py
│ │ ├── model.py # Template, Session models
│ │ ├── library.py # Template library manager
│ │ └── session.py # Session persistence
│ ├── web/
│ │ ├── __init__.py
│ │ ├── app.py # Dash application
│ │ ├── layout.py # UI layout components
│ │ ├── callbacks.py # Dash callbacks
│ │ └── assets/ # CSS, static files
│ ├── plugin/
│ │ ├── __init__.py
│ │ ├── registry.py # PluginRegistry
│ │ └── discovery.py # Entry point + directory scanning
│ └── script/
│ ├── __init__.py
│ └── api.py # Top-level scripting API
├── tests/
│ ├── test_io/
│ ├── test_channel/
│ ├── test_transform/
│ ├── test_criteria/
│ ├── test_protocol/
│ ├── test_plot/
│ └── fixtures/ # Sample MME data for tests
└── docs/
Scripting Examples
Quick injury summary
from impakt import Session
from impakt.criteria import hic, clip_3ms, nij, chest_deflection, femur_load
from impakt.protocol import euro_ncap
test = Session.open("./test_001")
results = {
"HIC15": hic(test, window_ms=15),
"3ms Clip": clip_3ms(test),
"Nij": nij(test),
"Chest Deflection": chest_deflection(test),
"Femur Left": femur_load(test, side="left"),
"Femur Right": femur_load(test, side="right"),
}
for name, r in results.items():
print(f"{name}: {r.value:.1f} {r.unit}")
rating = euro_ncap.evaluate(results, version="2024")
print(f"\nEuro NCAP: {rating.stars} stars ({rating.adult_pct:.0f}% adult)")
Batch comparison across tests
from impakt import Session
from impakt.plot import overlay
from pathlib import Path
tests = [Session.open(p) for p in Path("./tests").iterdir() if p.is_dir()]
overlay(
[t.channel("11HEAD0000ACXA").transform.cfc(1000) for t in tests],
labels=[t.metadata.test_number for t in tests],
title="Head X Acceleration Comparison",
corridors=["corridors/head_ax_corridor.csv"],
).to_pdf("head_comparison.pdf")
Custom math channel
from impakt import Session
from impakt.transform import math_expr
test = Session.open("./test_001")
custom = math_expr(
expression="0.6 * chest_x + 0.4 * chest_z",
channels={
"chest_x": test.channel("11CHST0000ACXA").transform.cfc(180),
"chest_z": test.channel("11CHST0000ACZA").transform.cfc(180),
},
name="Weighted Chest Metric",
unit="g",
)
custom.plot(title="Custom Weighted Chest Acceleration")
Multi-test overlay with cursors
from impakt import Session
from impakt.plot import overlay, cursor_values
test1 = Session.open("./test_001")
test2 = Session.open("./test_002")
fig = overlay([
test1.channel("11HEAD0000ACXA").transform.cfc(1000),
test2.channel("11HEAD0000ACXA").transform.cfc(1000),
])
vals = cursor_values(fig, x1=0.015, x2=0.062)
print(vals)
# channel value_at_x1 value_at_x2 delta
# 0 test_001/... 12.3 45.7 33.4
# 1 test_002/... 11.8 42.1 30.3
Launch web UI with template
from impakt.web import serve
from impakt import Session
test = Session.open("./test_001")
serve(test, template="frontal_ncap", port=8050)
# Opens browser at http://localhost:8050