stentfit
Submodules
Classes
A stent design, from its STL surface mesh to a fitted spline wireframe. |
|
A test artery: a wall surface, its centreline, and the 3D solid meshed from them. |
|
A mixed-dimensional beam-to-solid simulation setup for one stent and artery. |
Package Contents
- class stentfit.Stent(stl_file: str, stent_name: str, output_dir: str, n_points: int | None = None, max_display: int = 500000, remove_supports: bool = False, random_seed: int = 0, n_rings: int | None = None, auto_tune: bool = True, pixels_per_strut: int = 10, dilate_px: int = 3, pad_fraction: float = 0.2, tune_time_limit: int = 120, quality_gamma: float = 2.0, ring_halo_frac: float = 0.4)
A stent design, from its STL surface mesh to a fitted spline wireframe.
Holds one stent’s data as it moves through the skeletonisation pipeline. Every stage sets more attributes and writes its own inspectable intermediate (CSV + Plotly HTML) into
output_dir, so the run can be checked before committing to an expensive contact simulation.Run the whole thing with
skeletonize(), or drive the three phases separately for the interactive per-ring workflow:stent = Stent(stl_file, "stent01", "outputs/stent01").skeletonize() # or, phase by phase, inspecting the plots in between: stent = Stent(stl_file, "stent01", "outputs/stent01") stent.skeletonize_2d() # sample -> rings -> 2D skeleton + checkpoint stent.edit_and_assemble() # manual 2D fixes -> assembled flat skeleton stent.finalize() # wrap to 3D -> clean graph -> fit splines
After a kernel restart,
load()rebuilds the object from the checkpoint in an existing output folder, so the run can pick up atedit_and_assemble()without recomputing anything.The tuning parameters below are set once on the instance; per-operation parameters stay as arguments on the method that uses them.
- Parameters:
stl_file – Path to the stent surface mesh (STL).
stent_name – Name used to label outputs and plots.
output_dir – Folder for all outputs. If it already exists and is non-empty,
skeletonize_2d()asks whether to reuse, overwrite, or branch into a versioned folder, and may replace this with the folder actually used.n_points – Number of points to sample from the mesh.
Nonepicks the count automatically from the stent size.max_display – Maximum number of points drawn in the HTML views.
remove_supports – Drop print-support points during sampling.
random_seed – Seed for the point sampling, for repeatable runs.
n_rings – Expected ring count.
Nonelets ring detection decide.auto_tune – Search for the best 2D skeletonisation parameters per ring.
pixels_per_strut – Raster resolution, in pixels across one strut width.
dilate_px – Dilation radius, in pixels, before thinning.
pad_fraction – Seam padding added when unrolling a ring to 2D.
tune_time_limit – Time budget, in seconds, for the auto-tune search.
quality_gamma – Weight of the skeleton quality score during tuning.
ring_halo_frac – Z-halo, as a fraction of ring height, so struts reconnect across neighbouring rings.
- stl_file
- stent_name
- output_dir
- n_points = None
- max_display = 500000
- remove_supports = False
- random_seed = 0
- n_rings = None
- auto_tune = True
- pixels_per_strut = 10
- dilate_px = 3
- pad_fraction = 0.2
- tune_time_limit = 120
- quality_gamma = 2.0
- ring_halo_frac = 0.4
- mesh = None
- stent_df = None
- stent_features = None
- stent_centerline_direction = None
- ring_edges = None
- ring_order = None
- ring_2d = None
- skel_arc = None
- skel_z = None
- skel_px = None
- surf_df = None
- skeleton_df = None
- skeleton_curves = None
- skeleton_splines = None
- _versioned_candidates() list[str]
List the existing output folders belonging to this stent.
Matches
output_diritself plus any versioned siblings (<name>_v02,_v03, …) in the same parent folder.- Returns:
Sorted folder paths, empty if none exist yet.
- _pick_existing_output_dir(action: str = 'use') str
Resolve which of this stent’s versioned output folders to act on.
With no candidate found, returns
output_dirunchanged; with exactly one, returns it; with several, prints them and prompts the user to pick one. Used for both reusing and overwriting, so a stent with several versions never has one silently picked for it.- Parameters:
action – Verb used in the prompt, so it reads as the operation actually about to happen —
"use"or"overwrite".- Returns:
The resolved folder path to actually act on.
- _resolve_output_dir() bool
Settle on the output folder, asking the user if one already exists.
If
output_direxists and is non-empty, offers to reuse it as-is, overwrite (wipe) it, or branch into a new versioned folder (<name>_v02,_v03, …). Reusing loads that folder’s checkpoint straight onto this object instead of recomputing anything. Updatesoutput_dirin place, and creates the folder.Reusing and overwriting both go through
_pick_existing_output_dir(), so when several versions of this stent exist the user is asked which one — an overwrite never silently wipes the unversioned base folder while other versions sit alongside it.- Returns:
Trueif an existing checkpoint was loaded and the caller should skip recomputation,Falseto carry on with a fresh run.
- save_checkpoint(verbose: bool = True) str
Write the per-ring 2D skeletons and stent geometry to
ring_2d.pkl.Together with the already-saved
ring_points.csv, this letsload()rebuild the object after a kernel restart, without rerunning sampling, ring detection, or 2D skeletonisation.- Parameters:
verbose – Print the saved path and ring count.
- Returns:
Path to the written
ring_2d.pkl.
- _load_checkpoint_into(output_dir: str) None
Restore this object’s state from an output folder’s checkpoint.
- Parameters:
output_dir – Folder holding
ring_2d.pklandring_points.csv.
- classmethod load(output_dir: str, stl_file: str = '', stent_name: str = '', **kwargs) Stent
Rebuild a
Stentfrom an existing output folder.Reads back the
ring_2d.pklcheckpoint andring_points.csv, so the pipeline can resume atedit_and_assemble()after a kernel restart. This replaces the procedural pipeline’sstate=Noneresume branch — a reloaded object is the resumed state.The stent’s own geometry (features, centreline direction, ring edges, surface cloud) all comes from the checkpoint.
stl_fileonly matters if you intend to re-runskeletonize_2d()on the reloaded object.- Parameters:
output_dir – Folder holding
ring_2d.pklandring_points.csv.stl_file – Path to the original STL, if it is needed again.
stent_name – Name used to label outputs and plots. Empty uses the output folder’s basename.
kwargs – Any
Stenttuning parameter, to override the constructor defaults on the reloaded object.
- Raises:
FileNotFoundError – If the folder has no checkpoint to load.
- Returns:
The reconstructed stent.
- skeletonize_2d() Stent
Sample the stent mesh, detect its rings, and 2D-skeletonise each ring.
Loads the STL, samples a point cloud, splits the stent into rings, then runs the 2D skeletonisation per ring. The assembled ring data is checkpointed to disk (
save_checkpoint()) so the run can resume after a kernel restart.If
output_diralready exists and is non-empty, the user is asked whether to reuse it as-is, overwrite it, or branch into a new versioned folder. Reusing loads that folder’s checkpoint instead of recomputing anything.Sets
stent_df,stent_features,stent_centerline_direction,ring_edges,ring_2d, andring_order.- Raises:
FileNotFoundError – If
stl_filedoes not exist.- Returns:
self, so phases can be chained.
- edit_and_assemble() Stent
Apply the interactive manual 2D edits, then assemble the flat skeleton.
Runs after
skeletonize_2d(). Always prompts once to manually edit any ring, so defects the automatic detector missed can be fixed by hand on the flat ring skeleton, then concatenates every ring into one 2D skeleton.Sets
skel_arc,skel_z,skel_px, andsurf_df.- Returns:
self, so phases can be chained.
- finalize(prune_tip_frac: float = 0, max_display: int = 500000, random_seed: int = 0) Stent
Wrap the 2D skeleton to 3D, fit splines, and write the final exports.
Runs after
edit_and_assemble(). Lifts the assembled 2D skeleton onto the 3D stent surface (which also cleans up the graph: junction blobs contracted to a centroid, short dead-ends pruned), fits a B-spline per curve, then writes the final feature/view exports plus the unrolled-2D and trimesh-3D spline views.Sets
skeleton_df,skeleton_curves, andskeleton_splines.- Parameters:
prune_tip_frac – Fraction of each curve tip to prune after wrapping.
max_display – Maximum number of points drawn in the HTML views.
random_seed – Seed for any subsampling during the 3D wrap.
- Returns:
self, so phases can be chained.
- skeletonize(prune_tip_frac: float = 0) Stent
Run the full skeletonisation, from the STL mesh to fitted splines.
Chains all three phases:
skeletonize_2d(),edit_and_assemble(), thenfinalize(). Each writes its own intermediates intooutput_dir.(
skeletonize_2d()is only the first phase, despite the similar name — the verb alone means the whole run, the_2dsuffix marks the sub-step.)- Parameters:
prune_tip_frac – Fraction of each curve tip to prune after wrapping.
- Returns:
self, holding the 2D skeleton, the 3D skeleton graph, the grouped curves, and the fitted splines.
- plot_splines_2d() None
Draw the fitted splines on the unrolled (arc, z) plane.
Writes
skeleton_splines_2d.html/.pngintooutput_dir.
- plot_splines_trimesh() None
Draw the fitted splines as 3D tubes with trimesh.
Writes
skeleton_splines_trimesh.htmlandskeleton_splines.glbintooutput_dir.
- __repr__() str
- Returns:
A short summary of how far this stent has been processed.
- class stentfit.Artery(stent: stentfit.stent.Stent, artery_type: str = 'straight', inner_margin: float = 0.5, wall_thickness: float = 0.5, noise_amplitude: float = 0.05, noise_seed: float = 0, bend_angle_deg: float = 180.0, mesh_type: str = 'HEX8', artery_youngs: float = 2.0, n_circumference: int = 64, n_axial: int = 150)
A test artery: a wall surface, its centreline, and the 3D solid meshed from them.
The geometry is parametric and generated to fit a given stent, rather than imported from imaging — enough to exercise the whole mixed-dimensional chain end to end. The shape is a parameter, not a separate constructor:
artery = Artery(stent, artery_type="curved", inner_margin=0.5) sim = Simulation(stent, artery, sim_input_dir) sim.setup()
Everything is resolved and built at construction, so
radius,geometryandcentrelineare real the moment the object exists. Note this builds only the wall surface (atrimeshtube); the 3D finite-element solid 4C actually solves on is a separate step,mesh_solid(), whichsetup()runs for you.Every dimension is derived from the stent: the lumen radius is the stent’s outer radius plus
inner_marginclearance, the length a multiple of the stent length (so the stent always sits well inside), and any bend radius is picked so the arc roughly spans that length at the given bend angle. There is deliberately no way to set those dimensions by hand — a hand-sized tube is still a synthetic artery, and the real use for a specific geometry is importing patient anatomy, which is a different construction path.- Parameters:
stent – The stent this artery is sized to hold. Its skeletonisation must have run, so its features are populated.
artery_type – Shape:
'straight','curved', or's_bend'.inner_margin – Clearance, in mm, between the stent’s outer radius and the lumen wall.
wall_thickness – Wall thickness, in mm.
0builds the lumen surface only, with no separate wall.noise_amplitude – Fractional wall-roughness noise, as a fraction of the radius.
0gives a smooth pipe.noise_seed – Seed for the wall noise, for repeatable runs.
bend_angle_deg – Total bend angle, in degrees. Used only by
'curved'and's_bend'.mesh_type – GMSH element type for the solid:
'TET4','TET10', or'HEX8'. Used bymesh_solid().artery_youngs – Wall Young’s modulus, in MPa (placeholder StVenantKirchhoff material). Used by
mesh_solid().n_circumference – Number of vertices around each cross-section.
n_axial – Number of cross-sections along the length.
- Raises:
ValueError – If
artery_typeis unknown, or the stent has not been skeletonised yet.
- stent
- artery_type = 'straight'
- inner_margin = 0.5
- wall_thickness
- noise_amplitude = 0.05
- noise_seed = 0
- bend_angle_deg = 180.0
- mesh_type = 'HEX8'
- artery_youngs = 2.0
- radius
- length
- bend_radius = None
- solid_yaml: pathlib.Path | None = None
- _stent_features() dict
Read the stent’s geometry features, checking it has been skeletonised.
- Raises:
ValueError – If the stent’s pipeline has not run yet.
- Returns:
The stent’s features dict.
- _resolve_bend_radius(cap: float | None) float | None
Work out the arc radius for a bent artery.
Picked so the arc spans about 90% of the artery’s own length at the given bend angle, then limited by
capso a shallow angle cannot produce an arc so wide the artery looks straight. An S-bend splits its length across two arcs.- Parameters:
cap – Widest arc radius allowed, in mm.
Nonefor a straight artery, which has no arc.- Returns:
The arc radius in mm, or
Nonefor a straight artery.
- _build(n_circumference: int, n_axial: int) tuple
Build the wall surface mesh and the centreline it is swept along.
- Parameters:
n_circumference – Number of vertices around each cross-section.
n_axial – Number of cross-sections along the length.
- Returns:
(geometry, centreline)— thetrimeshwall surface and the(n, 3)centreline points.
- _print_summary() None
Print the built artery’s dimensions, matching the old pipeline’s output.
- mesh_solid(out_path: str | pathlib.Path, element_size: float, mesh_type: str | None = None, youngs_modulus: float | None = None, poisson_ratio: float = 0.3, density: float = 1.0, material_id: int = 1) pathlib.Path
Mesh the artery wall as a hollow 3D solid with GMSH and write a 4C
.yaml.This is the finite-element mesh 4C solves on, as opposed to the wall surface built at construction. Meshes the annulus between
radiusandradius + wall_thicknessas a straight tube, classifies its boundary nodes intoDSURFACEsets (1= lumen,2= inlet,3= outlet), then warps the whole tube ontocentrelineusing the same frame convention as the stent warp, so beam and solid stay aligned. Writes the mesh with a placeholderMAT_Struct_StVenantKirchhoffmaterial.Stores the written path on
solid_yaml, whichassemble()reads back.- Parameters:
out_path – File path the 4C
.yamlsolid is written to.element_size – Target element size, in mm.
mesh_type – Element type:
'TET4','TET10', or'HEX8'.Noneusesmesh_typefrom the constructor.youngs_modulus – Material Young’s modulus, in MPa.
Noneusesartery_youngsfrom the constructor.poisson_ratio – Placeholder material Poisson’s ratio.
density – Placeholder material density.
material_id – Material ID written into the 4C input.
- Raises:
ValueError – If the wall has no thickness, or the element type is not supported.
- Returns:
The path written, also stored on
solid_yaml.
- __repr__() str
- Returns:
A short summary of the artery’s shape and size.
- class stentfit.Simulation(stent: stentfit.stent.Stent, artery: stentfit.artery.Artery, sim_input_dir: str | pathlib.Path, stent_youngs: float = 200000.0, stent_poisson: float = 0.3, stent_density: float = 0.0, beam_class_label: str = 'Beam3rHerm2Line3', factor_solid: float = 1.5, factor_beam: float = 1.2, n_steps: int = 10, expansion_force: float = 0.0001)
A mixed-dimensional beam-to-solid simulation setup for one stent and artery.
Composes a
Stentand anArteryinto a runnable 4C input: the stent is meshed as 1D beams and warped onto the artery centreline, the artery wall is meshed as a 3D solid, the two are tied together with BeamMe’s mortar beam-to-solid coupling, and — provided the coupling assumptions hold — a static solver header, boundary conditions and a quasi-static radial expansion load are written out:artery = Artery(stent, artery_type="curved", inner_margin=0.5) sim = Simulation(stent, artery, "outputs/simulation/input") sim.setup()
The artery is built first and passed in, so every artery-shape and wall-material knob lives on
Arteryand everything here concerns the stent, the coupling, and the load. Build both against the same stent — the constructor rejects a mismatch.This is a smoke test, not the physics of the reference papers: the artery uses a placeholder
StVenantKirchhoffmaterial, coupling is tied meshtying rather than true contact, and the balloon is a simplified radial point force.- Parameters:
stent – The stent to deploy. Its skeletonisation must have run, since the beam mesh is built from the splines in its output folder.
artery – The artery to deploy into, already built by
Artery. Every artery-shape and wall-material parameter lives on that object, not here.sim_input_dir – Folder every generated
.4C.yamland.vtuis written into.stent_youngs – Stent beam Young’s modulus, in MPa.
stent_poisson – Stent beam Poisson’s ratio.
stent_density – Stent beam material density.
beam_class_label – BeamMe beam element type, either
'Beam3rHerm2Line3'or'Beam3rLine2Line2'.factor_solid – Safety factor sizing the artery solid element size relative to the beam diameter.
factor_beam – Additional safety factor sizing the beam element length beyond
factor_solid.n_steps – Number of load steps for the balloon expansion ramp.
expansion_force – Radial point-force magnitude for the balloon expansion.
- Raises:
ValueError – If
arterywas built for a different stent.
- stent
- artery
- sim_input_dir
- factor_solid = 1.5
- stent_youngs = 200000.0
- stent_poisson = 0.3
- stent_density = 0.0
- beam_class_label = 'Beam3rHerm2Line3'
- factor_beam = 1.2
- n_steps = 10
- expansion_force = 0.0001
- beam_mesh = None
- full_mesh = None
- coupling_report = None
- property beam_diameter: float
The beam cross-section diameter — the stent’s strut thickness, read straight off the composed stent, in mm.
- Type:
returns
- property solid_element_size: float
Target artery solid element size: the beam diameter with the
factor_solidsafety factor applied, in mm.- Type:
returns
- property beam_element_size: float
Target beam element length: the solid element size with the further
factor_beamsafety factor applied, in mm.- Type:
returns
- print_stent_summary() None
Print the stent’s key dimensions, as a sanity check before meshing.
The values come from the live
Stentobject, so unlike the procedural pipeline nothing is re-read fromstent_features.json/skeleton_points.csv. A stent restored withload()has no 3D skeleton in memory, so the node count is only printed when it is available.
- mesh_artery() pathlib.Path
Mesh the artery wall as a 3D solid, sized to this simulation’s stent.
Thin wrapper over
mesh_solid()that fills in the element size (which depends on the stent’s strut thickness, so the artery cannot work it out alone) and the output path.- Returns:
Path to the written
artery_solid.4C.yaml.
- align() Simulation
Mesh the straight stent as beams and warp it onto the artery centreline.
Builds the beam mesh from the stent’s fitted splines, represents the artery centreline as a BeamMe
CosseratCurve, then warps the straight stent onto it — rotating the stent’s own straight axis onto the curve’s tangent, and centring the stent’sz_min/z_maxmid-point on the curve’s arc mid-point. Writesstent_warped.4C.yaml.Sets
beam_mesh.- Returns:
self, so steps can be chained.
- assemble(lumen_surface_index: int = 0, bc_type=None, output_filename: str = 'artery_stent.4C.yaml') Simulation
Import the artery solid and tie the stent beam mesh to it, as one 4C input.
Imports the artery’s solid
.yaml(written bymesh_solid()), then couples it tobeam_meshwith BeamMe’s mortar beam-to-solid method, writing the combined 4C input file. Coupling defaults to tied meshtying; passbme.bc.beam_to_solid_surface_contactfor a real deployment simulation instead of this smoke test.Sets
full_mesh.- Parameters:
lumen_surface_index – Index into the artery solid’s surface sets for the lumen surface the beams couple to.
0is the lumen (DSURFACE 1, written first by the mesher).bc_type – BeamMe beam-to-solid coupling type.
Nonedefaults to tied meshtying.output_filename – Filename for the assembled 4C input, written into
sim_input_dir.
- Returns:
self, so steps can be chained.
- export_paraview(output_name: str = 'artery_stent_mesh') tuple | None
Export the assembled beam+solid mesh as separate
.vtufiles for ParaView.BeamMe’s
write_vtksplits beams and solid elements into two files by itself; this just names them and reports their paths.- Parameters:
output_name – Base filename;
_beam.vtu/_solid.vtuare appended.- Returns:
Noneif nothing has been assembled yet. Otherwise(beam_vtu, solid_vtu)— the paths to the two written files.
- check_coupling(stiffness_ratio_min: float = 10.0, length_ratio_min: float = 1, length_ratio_max: float = 6, length_ratio_accuracy_max: float = 8.0) dict
Check the mixed-dimensional beam-to-solid coupling assumptions.
Three checks, following Steinbrecher et al., each independent:
Stiffness — the beam must be much stiffer than the solid (
E_beam / E_solid >= stiffness_ratio_min), since the coupling assumes the solid deforms around an effectively rigid-ish beam.Solid size vs. beam diameter — the solid element size must be at least the beam’s cross-section diameter, the spatial-resolution limit the mortar coupling is only valid above.
Element length ratio — beam elements should be longer than solid elements, but not by too much: a valid band up to
length_ratio_accuracy_max, and a narrower optimal band up tolength_ratio_max.
The beam element length is measured from the meshed beams themselves (mean end-to-end chord), not from the requested target.
Sets
coupling_report.- Parameters:
stiffness_ratio_min – Minimum acceptable
E_beam / E_solid.length_ratio_min – Lower bound of both the valid and optimal
L_beam / L_solidbands.length_ratio_max – Upper bound of the optimal band.
length_ratio_accuracy_max – Upper bound of the valid band, above which coupling accuracy degrades.
- Raises:
ValueError – If the beam mesh has not been built yet.
- Returns:
The report dict, one entry per check plus
all_passed.
- plot_overview(show: bool = True) pathlib.Path
Draw the artery surface, its centreline, and the warped stent together.
Writes
stent_artery_view.htmlintosim_input_dir, and shows the figure inline when running in a notebook.- Parameters:
show – Try to display the figure inline as well as saving it.
- Returns:
Path to the written HTML view.
- write_input(out_path: str | pathlib.Path | None = None, total_time: float = 1.0, inlet_surface_index: int = 1, outlet_surface_index: int = 2, fix_stent_node: bool = True) pathlib.Path
Build a runnable, schema-validated 4C static simulation input.
Adds a static solver header and runtime VTK output, fixes the artery’s inlet and outlet surfaces (3 translational DOF, Dirichlet), and applies a quasi-static radial “balloon” expansion: a point force at each beam centreline node, directed radially outward from the artery centreline and ramped from 0 to
expansion_forceovern_stepsby a time function. Iffix_stent_node, one stent node is also pinned in translation to remove the stent’s rigid-body motion, since the radial forces alone do not constrain it.- Parameters:
out_path – File path the simulation input is written to.
Nonewritessimulation.4C.yamlintosim_input_dir.total_time – Total simulation time for the static solver.
inlet_surface_index – Index into the solid’s surface geometry sets for the inlet (fixed) surface.
outlet_surface_index – Index into the solid’s surface geometry sets for the outlet (fixed) surface.
fix_stent_node – Pin one stent centreline node’s translation, to remove rigid-body motion.
- Raises:
ValueError – If nothing has been assembled yet, or the imported solid is missing the inlet/outlet surface sets.
- Returns:
The path written.
- setup(show_plot: bool = True) Simulation
Prepare a runnable 4C input, from the stent and artery through to the load.
Chains the whole synthetic pipeline: prints the stent summary, meshes the stent as beams and warps it onto the artery centreline (
align()), meshes the artery wall as a 3D solid (mesh_artery()), assembles the beam-to-solid mesh (assemble()) and exports it for ParaView (export_paraview()). It then checks the coupling assumptions (check_coupling()), shows the overview plot, and — only if those checks pass — writes the runnable input (write_input()).Named
setuprather thanrunon purpose: it prepares a runnable 4C input, it does not execute the analysis. Running the solve is 4C’s job, external to this package.- Parameters:
show_plot – Display the artery/stent overview figure inline.
- Returns:
self, holding the meshes and the coupling report.
- __repr__() str
- Returns:
A short summary of how far this simulation has been set up.