# 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. --- ## 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 1. **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. 2. **Non-destructive transforms.** Filtering, alignment, and math operations produce new channel views. The transformation chain is stored and reproducible. 3. **Scriptable first.** Every operation available in the UI is accessible through a Python API. The web UI is a frontend to the same engine. 4. **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. 5. **Protocol-aware.** Built-in knowledge of Euro NCAP, US NCAP, and IIHS injury criteria, scoring thresholds, and report formats. 6. **Plugin-extensible.** Custom readers, transforms, injury criteria, report templates, and UI components can be registered through a plugin API. --- ## Architecture Overview ```mermaid 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. ```mermaid classDiagram class ReaderProtocol { <> +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.ini` for test-level metadata (vehicle, dummy, impact config) - Parse `.chn` headers for per-channel metadata (units, sample rate, CFC class, pre-trigger) - Read `.dat` files (ASCII or binary IEEE float) into NumPy arrays - Reconstruct time vectors from `dt` and pre-trigger sample count - Populate `TestData` and `Channel` objects --- ### impakt.channel — Channel Model The core data model. Channels are immutable value objects wrapping time-series data with rich metadata. ```mermaid 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. ```mermaid 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:** ```mermaid classDiagram class Transform { <> +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. ```mermaid classDiagram class InjuryCriterion { <> +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. ```mermaid 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 ```mermaid 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. ```mermaid 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 | ```mermaid 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): ```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`): ```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:** ```mermaid 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). ```mermaid 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. ```mermaid 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 { <> +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.cfg` entry point groups) - Directory scanning (`~/.impakt/plugins/`) - Explicit registration via API **Example plugin integration:** ```mermaid 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. ```python 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: ```mermaid 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 ```mermaid 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: ```mermaid 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 ```mermaid 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 ```mermaid 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. ```mermaid 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 ```python 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 ```python 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 ```python 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 ```python 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 ```python 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 ```