Command-Line Interface
Sentinel ships a sentinel command-line tool for the common run cases — running an inversion, validating a configuration, converting a legacy runfile, running a forward solve, and generating a mesh — without writing a Julia script. It is built on Comonicon and lives in the Sentinel.CLI submodule; the entry point is Sentinel.main.
Invoking the CLI
The repository provides a wrapper script at bin/sentinel that launches the CLI in the package's own project environment:
bin/sentinel --help
bin/sentinel <command> --help # help for a specific commandEquivalent forms, if you prefer to drive it from Julia directly:
# One-off, anywhere the Sentinel project is active
julia --project=/path/to/sentinel -e 'using Sentinel; Sentinel.main()' -- <command> [args...]# From the REPL or another script
using Sentinel
Sentinel.main(["invert", "config.toml"])Startup latency
Each invocation pays Julia's package-load/precompile latency (typically a few seconds to tens of seconds the first time). This is negligible next to a full inversion, but for many quick calls keep a REPL open and call Sentinel.main directly, or build a sysimage.
Commands at a glance
| Command | Purpose | Input |
|---|---|---|
invert | Run an MRE inverse reconstruction | .toml config or .dat runfile |
validate | Check a configuration without running | .toml config or .dat runfile |
convert | Convert a legacy runfile to TOML | .dat runfile |
forward | Run a forward solve | forward .dat runfile |
mesh | Generate a hex27 mesh from MRE data | .mat data file |
All commands accept -h / --help.
invert
Run a full inverse reconstruction. Accepts either a Sentinel TOML config or a legacy Fortran .dat runfile (the format is detected from the extension). A fully-resolved provenance *.config.toml is written next to the configured output stem; when the input is a .mat data file, the mesh and displacement files generated from it are written under generated_mesh/.
invert does not write the reconstruction
The reconstructed properties are not written to disk by this command. To get them out, run the reconstruction from Julia with solve and use the export helpers — see How to Export Results to MATLAB and ParaView.
bin/sentinel invert config.toml --threads 8 # multicore (auto)
bin/sentinel invert config.toml --basedir /data/study # resolve inputs here
bin/sentinel invert runfile.dat --quiet # legacy runfile, no chatter
bin/sentinel invert config.toml --gpu --threads 8 # prefer GPU gradient
bin/sentinel invert config.toml --serial # force serial| Argument / option | Meaning |
|---|---|
config | path to a .toml config or .dat runfile (required) |
--basedir <path> | directory that relative input paths resolve against (default: the config file's directory) |
--quiet | suppress the solver's per-iteration output |
--gpu | prefer a GPU gradient backend (Metal on Apple, CUDA on NVIDIA) |
--serial | force the serial per-zone path (no threads; e.g. low memory) |
--threads N | Julia threads (handled by the bin/sentinel wrapper; default: all cores) |
Parallelism is chosen automatically — see Parallelism below.
validate
Parse a configuration, print a summary of the resolved problem, and check that the referenced input files exist — without running the solve. Unknown keys and schema typos are reported as errors, so this is the fast way to catch a broken config before committing to a long run.
bin/sentinel validate config.toml
bin/sentinel validate runfile.dat --basedir /data/studyConfiguration OK: config.toml
model: isotropic
frequency: 100.037 Hz
mesh: T18v7p3.hom.nod (+ .elm/.bnd)
displacements: T18v7p3.dsp
output stem: inv/T18v7p3
properties: 3
regularizers: 4 (spatial_filter, cg_residual_scaling, van_houten, mcgarry_bounds)
iterations: global=100 zone=1
input files: found| Argument / option | Meaning |
|---|---|
config | path to a .toml config or .dat runfile (required) |
--basedir <path> | directory that input files are resolved against (default: the config file's directory) |
convert
Convert a legacy Fortran .dat runfile to the Sentinel TOML format (the human-editable config consumed by invert/validate). See Configuring Inverse Runfiles for the legacy format and runfile_to_toml for the underlying conversion.
bin/sentinel convert runfile.dat # writes runfile.toml
bin/sentinel convert runfile.dat --output cfg.toml| Argument / option | Meaning |
|---|---|
input | path to a legacy .dat runfile (required) |
--output <path> | output TOML path (default: the input path with a .toml extension) |
Note
Conversion is one-way (.dat → .toml). A configuration that enables the force-balance regularizer cannot be expressed in the TOML schema yet and is reported as an error — keep driving such runs from the legacy runfile.
forward
Run a forward MRE solve from a forward .dat runfile: assemble, apply boundary conditions, solve, and write the calculated displacement field as a .dsp file. Add --vtk to also write a <stem>.vtu for ParaView. For exporting an inversion's reconstructed properties (MATLAB/ParaView/ReconProps), see How to Export Results to MATLAB and ParaView.
bin/sentinel forward forward.dat
bin/sentinel forward forward.dat --output result/run1 --vtk| Argument / option | Meaning |
|---|---|
runfile | path to a forward .dat runfile (required) |
--basedir <path> | directory that relative input paths resolve against (default: the runfile's directory) |
--output <stem> | output path stem (default: the runfile's output stem) |
--vtk | also write <stem>.vtu for visualization |
Wraps parse_runfile → ForwardProblem → solve.
mesh
Generate a hex27 finite-element mesh from an MRE data .mat file. Reads the displacement/anatomy data, builds the mesh, and writes the NLI-format .nod/.elm/.bnd/.dsp files used by invert and forward.
bin/sentinel mesh scan.mat # writes scan.nod/.elm/.bnd/.dsp
bin/sentinel mesh scan.mat --output mesh/scan --strategy 2
bin/sentinel mesh scan.mat --format washu| Argument / option | Meaning |
|---|---|
matfile | path to an MRE .mat data file (required) |
--output <stem> | output path stem (default: the input path without its extension) |
--strategy <n> | mesh strategy — 1 = one node per voxel, 2 = target wavelength, 3 = target resolution (default: 1) |
--format <fmt> | input format, uiuc or washu (default: uiuc) |
Wraps read_mre_uiuc / read_mre_washu → generate_hex_mesh → write_hex_mesh_files. For finer control over mesh parameters (wavelength estimate, buffer, etc.), use HexMeshConfig programmatically — see How to Generate a Mesh from MRI Data.
Parallelism
The inversion decomposes the domain into overlapping zones solved per global iteration — that is the unit of parallelism. The strategy is chosen automatically from the material model and the Julia thread count (one shared policy, select_parallel_strategy, used by both the CLI and Sentinel.invert):
| Case | Path |
|---|---|
| Model 1 + threads or GPU | batched driver — threads zone solves/factorizations + the lockstep-secant line search; gradient as one batched kernel (GPU if available, else multicore CPU) |
| Any other model + threads | threaded per-zone — each zone's full solve runs on its own task |
| single thread, no GPU | serial |
So the only thing you normally set is the thread count (fixed at process start, so the bin/sentinel wrapper sets it from --threads N / JULIA_NUM_THREADS, default auto):
bin/sentinel invert config.toml --threads 8 # auto-selects the batched/threaded path--gpuprefers a GPU gradient (Metal/CUDA); for Model 1 this is the batched GPU kernel. Load happens on demand.--serialforces the serial path (escape hatch for memory-constrained runs).
The batched driver's gradient kernel is shear-modulus-specific, so the GPU/batched path is Model 1 only — other models automatically use the threaded per-zone path (which works for every model), and a single-threaded session runs serially.
For multi-process or multi-machine zone parallelism (any model), use the programmatic API with pmap_func=Distributed.pmap and/or distributed=true; see How to Run Inversions in Parallel.
Same policy from Sentinel.invert
The programmatic .mat one-shot Sentinel.invert chooses its gradient device with opts.compute_backend ("auto"/"gpu"/"cpu") and (once wired to the shared selector) the same batched-vs-threaded-vs-serial policy, so the CLI and invert parallelize consistently.
The TOML configuration
invert and validate consume a TOML config that maps 1:1 onto InversionProblem. A minimal example:
[data]
mesh = "T18v7p3.hom" # stem → .nod/.elm/.bnd (or nod/elm/bnd keys)
displacements = ["T18v7p3.dsp"]
frequency_hz = 100.037
output = "inv/T18v7p3"
[model]
type = "isotropic"
density_kg_m3 = 1000.0
[[regularization]]
kind = "spatial_filter"
sigma = 0.003
sigma_final = 0.0015
[[regularization]]
kind = "van_houten"
level = 1.2
[zones]
edge_m = 0.0195633
overlap = 0.15
[iterations]
global = 100
zone = 1To reconstruct directly from a .mat data file instead of pre-built mesh files, give [data] mat = "scan.mat" (the mesh is generated automatically). Relative paths are resolved against the config file's directory unless --basedir is given. Generate a starting config from an existing runfile with convert, or load/save programmatically via load_inversion / save_inversion.
Examples
From a .mat file (no download)
examples/run_mat_demo.sh starts an inversion directly from a .mat data file (the mesh is generated automatically) and runs out-of-the-box on the bundled test fixture:
examples/run_mat_demo.sh # bundled fixture, parallel (Model 1)
examples/run_mat_demo.sh --mat scan.mat # your own UIUC .mat
examples/run_mat_demo.sh --gpu # gradient on the GPUThe MGH dataset
examples/run_mgh_demo.sh drives the CLI end-to-end on the MGH test dataset — validate, optionally convert to TOML, then invert:
examples/run_mgh_demo.sh # quick 2-iteration run (default)
examples/run_mgh_demo.sh --full # full 100-iteration run
examples/run_mgh_demo.sh --threads 14 # set Julia threads (default: auto)
examples/run_mgh_demo.sh --toml # also show convert → TOML → runThe MGH dataset (~276 MB) is gitignored and obtained separately; if it is absent, the script prints where to place it and exits without running. See examples/README.md for the expected layout.
Optional: installing sentinel on your PATH
The bin/sentinel wrapper is sufficient for most users. If you want a sentinel command on your PATH, Comonicon can build one from the package:
using Sentinel
Sentinel.CLI.comonicon_install() # uses the settings in Comonicon.tomlThis builds a launcher (optionally backed by a sysimage) under your Julia depot's bin directory. See the Comonicon documentation for configuration options.
API
Sentinel.main Function
Sentinel.main(args=ARGS)Entry point for the sentinel command-line interface. Dispatches to the invert, validate, convert, forward, and mesh subcommands. Invoked by the bin/sentinel wrapper script; see docs/src/guide/cli.md.