Workflows
stentfit is built around three classes. Each one holds the data it produces,
so the way to read a workflow is: which method you call, which attributes it
fills in, and which files it leaves on disk.
The diagrams below follow the source (stent.py, artery.py,
simulation.py). Click any name to jump to its full API reference.
Stent
One stent, from its STL surface mesh to a fitted spline wireframe.
skeletonize() runs all three phases, and each
phase is also callable on its own for the interactive, per-ring workflow.
These are the attributes the object carries. They all start as None and the
phases below fill them in, which is what the “Fills in” columns refer to.
Attribute |
Holds |
|---|---|
|
the loaded trimesh surface mesh |
|
the sampled point cloud, in cylindrical and Cartesian coordinates |
|
the geometry values: |
|
the PCA long-axis unit vector |
|
the z-boundaries between rings |
|
the ring ids, bottom to top |
|
the per-ring 2D skeletons, keyed |
|
the assembled flat skeleton |
|
the pixel size behind each skeleton point |
|
the surface cloud used for the 3D wrap |
|
the final 3D skeleton graph |
|
the skeleton grouped into curves, as lists of point ids |
|
one fitted B-spline per curve |
The values you pass to the constructor, such as output_dir, auto_tune or
pixels_per_strut, are also attributes, but they are settings rather than
results so they are not listed here.
graph LR
subgraph P1[skeletonize_2d]
direction TB
A1[sample_stent_points] --> A2[detect_rings] --> A3[skeletonize_rings_2d] --> A4[save_checkpoint]
end
subgraph P2[edit_and_assemble]
direction TB
B1[edit_rings_2d_interactive] --> B2[assemble_2d_skeleton]
end
subgraph P3[finalize]
direction TB
C1[wrap_skeleton_to_3d] --> C2[fit_skeleton_splines] --> C3[save_stent_features_and_views]
end
S[["Stent(stl_file,<br/>stent_name,<br/>output_dir)"]] --> P1 --> P2 --> P3 --> D[["splines ready<br/>for meshing"]]
Phase 1 - skeletonize_2d()
Step |
Fills in |
Writes |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
Phase 2 - edit_and_assemble()
Step |
Fills in |
Writes |
|---|---|---|
|
updated |
|
|
This phase prompts once, so you can fix any ring the detector got wrong before
the skeleton is lifted to 3D. After a kernel restart,
load() rebuilds the object from ring_2d.pkl
and you can carry on from here without recomputing phase 1.
Phase 3 - finalize()
Step |
Fills in |
Writes |
|---|---|---|
|
|
|
|
|
|
|
||
|
||
|
skeleton_curves and skeleton_splines are both kept because they are
different things: the curves are the topology, meaning which skeleton points
form each strut, and the splines are the smooth geometry fitted through them.
Artery
A parametric test artery sized to hold a given stent. Everything is resolved in the constructor, so the object is complete as soon as it exists.
Attribute |
Holds |
|---|---|
|
the stent this artery was sized against |
|
the resolved dimensions, in mm |
|
the wall surface mesh, as a trimesh tube |
|
the |
|
the path to the 4C solid, |
The shape settings you pass in, such as artery_type, inner_margin and
wall_thickness, are kept as attributes too.
graph LR
S[["Stent<br/>(skeletonised)"]] --> A[["Artery(stent,<br/>artery_type,<br/>inner_margin)"]]
A --> G[radius, length, bend_radius]
G --> M[geometry + centreline<br/>wall surface]
M --> MS[mesh_solid]
MS --> Y[["artery_solid.4C.yaml<br/>3D solid mesh"]]
Step |
Fills in |
Writes |
|---|---|---|
|
|
|
|
|
The dimensions all come from the stent. The lumen radius is the stent’s outer
radius plus inner_margin, the length is a multiple of the stent length so the
clamped ends sit clear of the stent, and any bend radius is picked so the arc
spans most of that length.
Note that the constructor builds only the wall surface, which is a trimesh
tube. The finite-element solid that 4C actually solves on comes from
mesh_solid(), and
setup() calls that for you.
Simulation
Composes a stent and an artery into a runnable 4C input.
setup() runs the whole chain, and
every step is also callable on its own.
Attribute |
Holds |
|---|---|
|
the two composed objects |
|
the folder every generated file goes into |
|
the warped stent beam mesh |
|
the combined beam and solid mesh |
|
the pass/fail checks, including |
Only the last three are results; the rest are what you passed in. Nothing is
copied from the composed objects, so the stent features are read through as
sim.stent.stent_features and the solid path as sim.artery.solid_yaml.
graph TD
I[["Simulation(stent, artery,<br/>sim_input_dir)"]] --> A[align]
A --> B[mesh_artery]
B --> C[assemble]
C --> D[export_paraview]
D --> E[check_coupling]
E -->|all_passed| F[write_input]
E -.->|fails| X([skip: fix element sizes or moduli])
Step |
Fills in |
Writes |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
||
|
||
|
||
|
align() meshes the straight stent as
beams with mesh_skeleton_beams(), then warps it
onto the artery centreline.
assemble() ties the beams to the lumen
surface with import_artery_solid() and
assemble_beam_solid().
write_input only runs when all three coupling checks pass, because an input
file that breaks the mixed-dimensional assumptions should not look runnable.
Element sizing
Both factors are relative to the stent’s strut thickness, and their ratio is
what check_coupling() tests:
solid_element_size = strut_thickness × factor_solid
beam_element_size = strut_thickness × factor_solid × factor_beam
They are read-only properties, so they always follow the stent rather than drifting from it.
Coupling checks
Following Steinbrecher et al., where the beam is one stent strut and the solid is the artery wall:
# |
Check |
Criterion |
|---|---|---|
1 |
Stiffness ratio |
|
2 |
Rule of thumb |
|
3 |
Element length ratio |
|
See the README for what each output file contains, and the API reference for full signatures.