What is Hopki?

Hopki analyzes split Hopkinson (Kolsky) bar experiments in compression and tension. It is a from-scratch Python port of a legacy MATLAB GUI (twobar_g), validated to reproduce that code's reference outputs to within ~1×10-8.

It does two things: convert raw Nicolet oscilloscope .WFT captures into text waveforms, and turn the incident / reflected / transmitted gauge signals into forces, velocities, displacements, strain rate, and engineering & true stress–strain.

numpy / scipy backend gold-validated PySide6 + pyqtgraph GUI MIT licensed

Internally it is a two-stage pipeline. Stage 1 parses raw Nicolet .WFT binary captures into .FLT text. Stage 2 runs the analysis. The numeric core is completely GUI-free and deterministic: every human-in-the-loop choice from the original MATLAB GUI is an explicit function parameter, so a run is fully reproducible and unit-testable — the GUI only collects your picks and redraws.

The whole pipeline is validated to reproduce the original's reference outputs to within ~1×10-8. The core / CLI install is light (numpy + scipy + matplotlib); the desktop GUI is an optional extra (uv sync --extra gui).

Quick start

Dependencies and Python 3.12 are managed with uv.

uv sync                 # core + CLI tools
uv sync --extra gui     # also the desktop GUI (PySide6, pyqtgraph)

uv run hopki-gui                      # launch the GUI
uv run hopki-gui tests/analysis       # ...auto-loading an experiment

Command-line tools

Add --help to any tool for the full flag list.

Prebuilt binaries

No Python needed — download a desktop build for your OS from the project's Releases page (macOS .app, Windows .exe, Linux binary). They are unsigned, so on first launch: macOS → right-click the app → Open; Windows → SmartScreen → More infoRun anyway.

Run the tests

Tests use the stdlib unittest runner; address modules by dotted path (GUI tests run headless):

uv run python -m unittest tests.analysis.test_twobar
QT_QPA_PLATFORM=offscreen uv run python -m unittest tests.gui.test_controller

Build a binary yourself

uv sync --extra gui --extra packaging
uv run python packaging/build.py        # macOS/Windows/Linux (Nuitka)

The analysis pipeline

An experiment directory holds three config files (incid.exp, incid.spec, DAMP_F) and two signal files (incident bar gauge & transmitted bar gauge). The pipeline is four stages, each driven by an operator choice you make in the GUI:

  1. Window the incident / reflected / transmitted pulses around the pulse start tim_cut — reflected returns after 2·x1/c0, transmitted after (x1+x2)/c0.
  2. Dispersion + damping correction in the frequency domain, using the Bancroft phase-velocity table at Poisson ratio ν (default 0.29) and DAMP_F.
  3. Align the reflected pulse reflect_shift (samples).
  4. Mechanics: voltage→strain, forces, velocities, displacements, engineering & true stress–strain.
Signal polarity. The incident pulse must be compressive (negative). Some WFT→FLT converters emit the opposite sign — if the pulses look upside-down or the striker velocity comes out negative, tick invert signal polarity.

Configuration files

All of these are editable live in the GUI (Analysis tab) with a Revert button to reload them from disk.

The corrections, precisely

  • Dispersion applies a per-frequency phase shift ±2πf·x·(1/c0 − 1/c(f)) from the Bancroft phase-velocity table, plus an amplitude term exp(±DAMP_F·x).
  • Voltage→strain is 4·v / (gfact·vbridge); forces, velocities and displacements (cumulative-trapezoid integration) follow from there.

Two values are derived, not read from config

tpp (sampling interval) always comes from the signal's own time column. x1/x2 (gauge→specimen distances) are auto-estimated from the signals when config omits them: x1 is robust (matched-filter the incident window against the bar channel to find the reflected return at 2·x1/c0); x2 is best-effort — the transmitted pulse is weak, so its onset is biased late. The GUI flags an estimated x2 with a confidence (SNR) so you can verify it, re-estimate, or set it by 2 clicks on the Signals plot.

The GUI — four tabs

Analysis

Explore

Plot any derived quantity (strain rate, velocities, displacements, forces, striker velocity, stresses) against time or true strain. Flip the x-axis, negate either axis, and send the shown curve to Curve cleanup or Figures.

Curve cleanup

Pull a σ–ε curve from the analysis (or load a file), then zero, cut/crop (drag the pointer), smooth (Butterworth cutoff), straighten the toe (two clicks), read a slope (two clicks), negate an axis, undo, and save <name>_corr. Send the result to Figures.

Figures

Assemble publication figures from one or more curves.

  • Analysis — geometry helpers: Re-estimate x1, x2 re-derives both distances from the signals (handy when the TOML values are stale); Pick x2 sets it by clicking the incident then transmitted arrivals. Save config writes a per-experiment hopki.toml of just the fields you changed.
  • Cleanup — interactive smoothing: Smooth… shows the stress power spectrum with a draggable cutoff and a live filtered overlay; the Butterworth is only committed on Apply filter. Also scale/shift either axis by a constant, and → True σ–ε to derive the true curve from an engineering one.
  • Figures — publication export: per-curve x/y display scale constants (unit conversion, non-destructive); export renders through matplotlib with optional SciencePlots journal styles and LaTeX titles (falls back to mathtext if no TeX is installed).

File formats

FileWhat
.WFTRaw Nicolet oscilloscope binary waveform (input).
.FLT / .inc / .traTwo-column value time text signals.
output vectorsMATLAB-style -ascii text (f_in, s_e_true, e_dot…).
.npz figureCurves + styling/metadata, reloadable into the Figures tab.

Config-file layout. The legacy files are whitespace-delimited, one value per line: incid.exp is positional (nlong, npoint, tpp, Øbar, E, x1, x2, c0, gfact, vbridge) and incid.spec is (Øspec, Lspec, tdelayµs, pretrig%), with optional inline %comments. The two-line .FLT header is dropped in the .inc/.tra form (the data rows are identical).

Layered TOML. A hopki.toml can live next to an experiment (and a shared one higher up a campaign tree); the per-experiment file overrides the campaign one, and Save config writes only the fields you actually changed.

Good to know

  • Polarity is the first thing to check. If the striker velocity is negative or the incident pulse points up, toggle invert signal polarity — different WFT→FLT converters disagree on sign.
  • x2 is best-effort. Treat an auto-estimated x2 as a starting point: confirm it against the transmitted arrival (or set it with Pick x2) before trusting the stresses.
  • Poisson ratio. The dispersion correction defaults to ν = 0.29; adjust it for your bar material if needed.
  • Unsigned binaries. The downloadable apps aren't code-signed, so the OS warns on first launch — see Quick start → More details for the one-time bypass.

License

Hopki is released under the MIT License — free to use, modify, and distribute (including commercially), provided the copyright notice is kept.

No warranty. The software is provided “as is”, without warranty or liability of any kind. Always sanity-check results against known references before publishing.

© 2026 Shmuel Osovski

MIT License

Copyright (c) 2026 Shmuel Osovski

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.