Postprocessing
Visualization, convergence analysis, and property interpolation tools. For practical export workflows see Visualization & Data Export.
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")