Postprocessing
Visualization, convergence analysis, and property interpolation tools. For practical export workflows see How to Export Results to MATLAB and ParaView.
VTK Export
Export meshes and results to VTK format for visualization in ParaView.
Sentinel.export_vtk Function
export_vtk(filename::AbstractString, result::HexMeshResult;
write_disp::Bool=true, write_anatomical::Bool=true,
write_region::Bool=true)Export a HexMeshResult to VTK (.vtu) format with hex27 cells.
Arguments
filename: output path (without.vtuextension)result: hex mesh generation resultwrite_disp: include displacement point data (default: true)write_anatomical: include anatomical magnitude point data (default: true)write_region: include region assignment point data (default: true)
Notes
Writes a single .vtu (the .vtu suffix is added). Displacement is written as disp_real/disp_imag vector point data (metres); anatomical magnitude and region id as scalar point data. Open directly in ParaView.
Example
export_vtk("mesh", hex_mesh_result; write_disp=true)export_vtk(filename::AbstractString, grid, dh, disp::Displacement;
dispset::Int=1)Export a Ferrite grid with displacement solution to VTK.
Arguments
filename: output path (without.vtuextension)grid: Ferrite Griddh: DofHandlerdisp: solved displacementdispset: which displacement set to export (default: 1, 1-indexed)
Notes
Writes disp_real/disp_imag vector point data in metres for the chosen dispset (only the first min(nd, nn) nodes are populated). Cell type follows the grid (hex27/hex8/tet4). Use ParaView's Glyph filter to draw the vectors.
Example
disp = solve(forward_problem)
export_vtk("forward", forward_problem.grid, forward_problem.dh, disp)export_vtk(filename::AbstractString, material::Material, mesh::MaterialMesh;
prop_indices=1:material.numprop, val_idx::Int=1)Export material properties on a MaterialMesh to VTK (.vtu).
Nodal-basis properties are written as point data; elemental-basis properties are written as cell data.
Arguments
filename: output path (without.vtuextension)material: Material with property arraysmesh: MaterialMesh (structured hex8)prop_indices: which properties to export (default: all)val_idx: which value-per-point column (default: 1)
Notes
Values are written in physical units — the property's scalar factors are applied (Pa for shear/bulk, kg/m³ for density). Fields are named prop{i}_real/prop{i}_imag; nodal-basis properties become point data, elemental-basis become cell data.
Example
result = solve(prob)
mesh = result.setup.meshes[result.setup.config.meshind[1, 1]]
export_vtk("recon", result.material, mesh) # property 1 (shear, Pa) for Model 1Sentinel.PropertyMapData Type
PropertyMapDataExtracted material property coordinates and values (no file I/O).
Fields
coords::Matrix{Float64}:(n, 3)point coordinates (metres)values::Dict{String, Vector{Float64}}: named property arrays in physical units (Pa for shear/bulk, kg/m³ for density — thescalarfactors are applied), keyed"prop{i}_real"/"prop{i}_imag". Nodal-basis properties have one entry per node (nn); elemental-basis have one per element (ne).mesh_type::Symbol::hex8,:hex27, or:structured
Convergence Analysis
Sentinel.print_convergence_table Function
print_convergence_table(history::ConvergenceHistory; io::IO=stdout, header::Bool=true)Print a formatted convergence table showing per-iteration metrics.
Columns: Iteration | Objective | Disp Error | Mat Epsilon | Obj Ratio. Units: objective is unitless, disp_error is in metres, mat_epsilon is the unitless relative material change.
The objective ratio is objective[i] / objective[i-1] (shown as --- for iteration 1).
Sentinel.convergence_statistics Function
convergence_statistics(history::ConvergenceHistory) -> NamedTupleCompute summary statistics from a convergence history.
Returns
Named tuple with fields:
iterations: total iterationsconverged: whether the optimizer convergedinitial_objective: first iteration objectivefinal_objective: last iteration objectiveobjective_reduction:final / initialinitial_disp_error: first iteration displacement errorfinal_disp_error: last iteration displacement errorfinal_mat_epsilon: last iteration material changemin_objective: minimum objective across all iterationsmin_objective_iter: iteration at which minimum occurredavg_obj_reduction_rate: geometric mean of per-iteration ratios
Sentinel.export_convergence_csv Function
export_convergence_csv(filename::AbstractString, history::ConvergenceHistory;
delimiter::Char=',')Export convergence history to a CSV file.
Columns: iteration, objective (unitless), disp_error (metres), mat_epsilon (unitless relative material change).
Example
export_convergence_csv("convergence.csv", history)Property Interpolation
Interpolate reconstructed material properties from the material mesh to arbitrary points or structured grids.
Sentinel.PropertyGridResult Type
PropertyGridResultResult of interpolate_to_grid: material properties sampled on a regular structured grid.
Fields
data::Dict{String, Array{Float64, 3}}: property arrays keyed"prop{i}_real"/"prop{i}_imag"(e.g."prop1_real"). Values are in physical units — thematerial.prop[i].scalarfactors are already applied (Pa for shear/bulk, kg/m³ for density). Grid points with no reconstruction areNaN. To recover the raw normalized values, divide by the property's scalar.origin::SVector{3, Float64}: grid origin (metres).resolution::SVector{3, Float64}: voxel size (metres).dims::NTuple{3, Int}: grid dimensions(nx, ny, nz).
Sentinel.interpolate_properties Function
interpolate_properties(material::Material, mesh::MaterialMesh,
target_coords::AbstractMatrix{<:Real};
prop_indices=1:material.numprop, val_idx::Int=1)
-> Dict{String, Vector{Float64}}Interpolate reconstructed material properties from a MaterialMesh to arbitrary target points (use interpolate_to_grid for a regular grid).
Arguments
material: reconstructedMaterial.mesh: sourceMaterialMesh(structured hex8 grid).target_coords:(n, 3)matrix of target(x, y, z)coordinates in metres.prop_indices: which properties to interpolate (default: all).val_idx: which value-per-point column to use (default: 1).
Nodal properties are interpolated with the trilinear hex8 basis; elemental properties take the containing element's constant value.
Returns
Dict{String, Vector{Float64}} with keys "prop{i}_real" / "prop{i}_imag" (each length n). Values are in physical units — the material.prop[i].scalar factors are applied (Pa for shear/bulk, kg/m³ for density). Points outside the mesh extent (or in empty elements) are NaN.
Example
pts = [0.0 0.0 0.0; 0.01 0.0 0.0] # (n, 3), metres
vals = interpolate_properties(result.material, mesh, pts)
vals["prop1_real"] # μ′ (Pa) at each pointSentinel.interpolate_to_grid Function
interpolate_to_grid(material::Material, mesh::MaterialMesh,
origin, resolution, dims::NTuple{3, Int};
prop_indices=1:material.numprop, val_idx::Int=1)
-> PropertyGridResultSample reconstructed material properties onto a regular structured grid (e.g. an image voxel grid) by trilinear interpolation over the source hex8 material mesh.
Arguments
material: reconstructedMaterial.mesh: sourceMaterialMesh(the structured hex8 grid carrying the property).origin:(3,)grid origin[x0, y0, z0]in metres.resolution:(3,)voxel size[dx, dy, dz]in metres.dims:(nx, ny, nz)output grid dimensions.prop_indices: which properties to sample (default: all). Each property may live on a different material mesh (seemeshind), so when properties differ pass the mesh that carries the selected property — e.g.meshes[meshind[1, i]]together withprop_indices=[i]— rather than sampling all properties against a singlemesh.val_idx: which value-per-point column to use (default: 1).
Returns
A PropertyGridResult. Its data["prop{i}_real"] / data["prop{i}_imag"] are (nx, ny, nz) Float64 arrays in physical units — the property's scale factors (material.prop[i].scalar) are already applied, so shear/bulk are in Pa and density in kg/m³. Grid points outside the mesh extent, or in empty elements, are left as NaN (an honest "no value" sentinel, not zero).
Example
# material mesh carrying property 1 (shear), then sample it on its own grid:
mesh = result.setup.meshes[result.setup.config.meshind[1, 1]]
g = interpolate_to_grid(result.material, mesh,
collect(mesh.origin), collect(mesh.res), mesh.dims;
prop_indices = [1])
mu_re = g.data["prop1_real"] # μ′ (Pa), (nx,ny,nz), NaN outside the meshSee also interpolate_properties (arbitrary points) and export_reconprops (standard ReconProps.mat).
MATLAB / ReconProps Export
Write the standard ReconProps.mat consumed by the MATLAB MRE-analysis pipeline.
Sentinel.export_reconprops Function
export_reconprops(material::Material, setup::InverseProblemSetup,
basedir::String, outpath::String; iteration::Int=0) -> StringWrite the standard ReconProps.mat consumed by the MATLAB MRE-analysis pipeline. Interpolates the reconstructed properties onto the MR voxel grid (via interpolate_to_grid), applies the brain mask, computes derived quantities, and writes the .mat file. Returns outpath.
For ad-hoc grids you define yourself (no sidecar files required), use interpolate_to_grid with MAT.matwrite instead.
Arguments
material: reconstructedMaterial(current property values).setup: theInverseProblemSetupfrom the solve (e.g.result.setup) — provides the meshes, config, and model.basedir: dataset directory containing the required sidecar files.outpath: output.matpath.iteration: iteration number, recorded as metadata (default 0).
Required sidecar files (in basedir)
<stem>.InterpLocations.mat—xout,yout,zout,maskint(MR voxel grid and brain mask).<stem>.InterpData.mat—MagIm_int(magnitude image).the dataset's
.meshindfile.
Output .mat keys
3-D arrays on the MR voxel grid, in physical units, with voxels outside the mask set to NaN:
RealShear,ImagShear— storage / loss shear modulus μ′, μ″ (Pa).ShearMag— complex shear magnitudesqrt(RealShear^2 + ImagShear^2)(Pa).RealDens,ImagDens— density ρ (kg/m³), when reconstructed.RealBulk/ImagBulk(isotropic-incompressible) orRealLambda/ImagLambda(isotropic-compressible) — the third property, when present.DR— damping ratio0.5*(μ″/μ′ − ρ″/ρ′)(dimensionless).RC— reconstruction confidence0.5*(μ″/μ′)/DR(dimensionless).MagIm— the magnitude image fromInterpData.mat.meshind(2 × numprop),meshres(nmesh × 3, metres),recondir.
Example
result = solve(prob)
export_reconprops(result.material, result.setup, "/path/to/dataset", "ReconProps.mat")