◆ FORGE Suite
GitHubMechanical Neuroimaging Lab · Univ. of Delaware
Skip to content

Inversion Setup & Solve

The high-level programmatic API for describing and running an MRE inverse reconstruction: build an InversionProblem, call solve, and read the InversionResult. Configurations load and round-trip through load_inversion / save_inversion, and legacy Fortran runfiles convert with runfile_to_toml.

Problem definition

An InversionProblem is the declarative description of a reconstruction; PropertySpec describes one reconstructed material property.

Sentinel.InversionProblem Type
julia
InversionProblem(; kwargs...)

Validated, keyword-constructed description of an MRE inverse reconstruction.

Data (required)

  • frequency: actuation frequency (Hz, or Unitful)

  • displacements: .dsp file path(s), or

  • data: a UIUC-format .mat file — the mesh and displacement files are generated automatically at solve time (mesh_strategy selects 1/2/3)

  • mesh: mesh file stem ("study"study.nod/.elm/.bnd) or a NamedTuple (nod=..., elm=..., bnd=...); required with displacements, omitted with data

Physics

  • model = :isotropic: material model (Symbol, id, or model instance)

  • density = 1000.0: tissue density (kg/m³, or Unitful)

  • properties = default_properties(model; density): Vector{PropertySpec}

  • material_mesh_resolutions: vector of 3-tuples (m), default fine/medium/coarse

Regularization

  • regularization = default_regularization(): Vector{RegularizationSpec}

  • const_reg_iters = 30: iterations before the scheduled regularization weights activate (weights are 0 through this iteration, then ramp initial → final); must be < max_global_iter × max_zone_iter for ramped specs to contribute

Zones & iterations

  • zone_edge = 0.0195633 (m), zone_overlap = 0.15

  • max_global_iter = 20, max_zone_iter = 1

  • global_tol = zone_tol = line_tol = 0.001 (fractional)

  • zone_structures = [(1,0,0,1), (2,0,0,2), (3,0,0,2)] (CG/GN/QN/LS iters), structure_limits = [10, 150] (one fewer than structures)

Execution

  • output = "sentinel_inv": output file stem

  • solver = :default: :default (cached direct), :direct, :mumps, :krylov, or an AbstractLinearSolver instance

  • regstack = nothing: region stack file (required by SoftPrior)

  • initial_solution = nothing, verbose = true, calc_variance = false

  • mesh_strategy = 1 (data mode only), mumps_procs = 1, max_ram (MB)

source
Sentinel.PropertySpec Type
julia
PropertySpec(; name=:property, basis=:nodal, nvpp=1, scalars,
               mesh=1, reconstruct=false)

Description of one material property.

  • basis: :nodal or :elemental

  • nvpp: parameter values per material point

  • scalars: (real, imag) property scalars (native runfile units)

  • mesh: material-mesh index, or (real_mesh, imag_mesh)

  • reconstruct: whether to reconstruct, or (real, imag) separately

source

Running a solve

solve runs the reconstruction. It accepts either an InversionProblem or the path to a TOML configuration (the latter loads and solves in one step) and returns an InversionResult.

Sentinel.solve Function
julia
solve(problem::ForwardProblem) -> Displacement

Assemble stiffness, apply Dirichlet BCs, and solve the forward problem for all displacement sets.

Returns a Displacement containing the computed displacement and pressure fields.

source
julia
solve(prob::InversionProblem; basedir=".", on_iteration=nothing, kwargs...) -> InversionResult

Run the inverse reconstruction described by prob and return an InversionResult.

Arguments

  • basedir: directory that relative input paths in prob are resolved against.

  • on_iteration: optional callback invoked once per global iteration — after zone consolidation, before the convergence check — with the signature (giter::Int, material, state), where material is the current reconstruction and state.global_obj / state.disp_error / state.converged are the running metrics. For monitoring only; mutating material is not supported.

  • remaining kwargs are forwarded to run_inverse_solve! — e.g. pmap_func, gradient_backend, cudss_batched_solver, distributed.

Side effects

Writes the fully-resolved configuration to "<output>.config.toml" (next to the outputs) for provenance. When prob reconstructs directly from a .mat data file, the generated displacement mesh is written under dirname(output)/generated_mesh/.

Example

julia
prob   = InversionProblem(data="scan.mat", frequency=60.0, model=:isotropic, output="inv/scan")
result = solve(prob; on_iteration=(g, _m, s) -> @info "global iter" g obj=s.global_obj)

A convenience overload, solve(path::AbstractString; kwargs...), loads a TOML configuration with load_inversion and solves it in one step.

source
julia
solve(path::AbstractString; kwargs...) -> InversionResult

Convenience overload: load a TOML configuration with load_inversion and solve it in one step — equivalent to solve(load_inversion(path); basedir=dirname(abspath(path)), kwargs...). Relative paths in the file are resolved against the file's directory; kwargs are forwarded to solve (e.g. on_iteration). Returns an InversionResult.

Example

julia
result = solve("config.toml")
source

Result

Sentinel.InversionResult Type
julia
InversionResult

The result of solve: the reconstructed material, the final calculated displacement, the solver state, and the handles needed to export or post-process the reconstruction.

Fields

  • material::Material — reconstructed properties. The per-property arrays material.prop[i].rvalue / .ivalue are the normalized nodal/elemental values; multiply by material.prop[i].scalar[1] (real) and scalar[2] (imag) to get physical units — Pa for shear/bulk, kg/m³ for density. For Model 1, prop[1] is the complex shear modulus (μ′ real part, μ″ imaginary part). (interpolate_to_grid and export_reconprops apply this scaling for you.)

  • displacement::Displacement — the final calculated complex displacement field (metres) on the FE nodes.

  • state — the solver state: state.converged::Bool, state.global_obj (the objective: displacement misfit + regularization penalties), and state.disp_error (the displacement misfit).

  • problem::InversionProblem — the problem exactly as solved (input provenance).

  • setup::InverseProblemSetup — meshes, DOF handler, etc.; the handle you pass to interpolate_to_grid / export_reconprops for export.

Example

julia
result = solve(prob)
result.state.converged          # did it converge?
result.state.disp_error         # final displacement misfit

# physical shear modulus (Pa) on the material mesh, property 1:
p       = result.material.prop[1]
mu_real = p.scalar[1] .* p.rvalue    # μ′ (Pa)
mu_imag = p.scalar[2] .* p.ivalue    # μ″ (Pa)

See also solve, interpolate_to_grid, export_reconprops.

source

Configuration files

Load, save (provenance), and convert TOML / legacy .dat configurations. load_inversion reads both TOML files and legacy Fortran .dat runfiles directly, returning an InversionProblem either way.

Sentinel.load_inversion Function
julia
load_inversion(path) -> InversionProblem

Read a TOML configuration or a legacy Fortran .dat runfile and return an InversionProblem. Dispatches on the .dat extension: .dat paths are parsed with parse_inverse_runfile and raised via raise_config; any non-.dat path is treated as TOML. Unknown TOML keys raise errors (typo safety); omitted keys take the InversionProblem defaults.

source
Sentinel.save_inversion Function
julia
save_inversion(path, prob::InversionProblem)

Write the fully-resolved problem (all defaults made explicit) as TOML.

source
Sentinel.runfile_to_toml Function
julia
runfile_to_toml(runfile, toml=replace(runfile, r"\.dat$" => "") * ".toml")

Convert a legacy Fortran .dat runfile to the TOML format. Returns the output path.

source