Offset stenosis simulation#

As a first tutorial, we will illustrate major functionalities of VaSP with a non-axisymmetric stenosis model. This stenosis model is a synthetic geometry that can be described analytically and has been long used within fluid mechanics research [VFF07]. Due to the stenotic region with sudden area reduction, the flow becomes turbulent quickly without complex geometry or high Reynolds number. Additionally, eccentricity ensures that the simulation is deterministic, avoiding uncertainty associated with the symmetric geometry. Those features enable us to quickly develop and test the functionalities with reasonable computing time.

Mesh generation#

Note

An input mesh, offset_stenosis.stl, can be downloaded here.

There are couple of methods for determining the size of each tetrahedral cell, but VaSP uses centerline diameter by default. Details on different meshing strategy can be found here.

With that in mind, one can generate the mesh as follows:

vasp-generate-mesh -i offset_stenosis.stl -f True -fli 0 -flo 4 -c 3.8 -nbf 1 -nbs 1 -sch5 0.001

Here, -f is a boolean argument determining whether to add flow extensions or not, as explained in here. In this case, -flo 0 indicates no flow extension at the inlet, but -flo 4 will add flow extensions at the outlet with length equal to four times the length of radius of the outlet. -c is a coarsing factor determining the density of the mesh, where c > 1 will corasen the mesh. -nbf and -nbs are the number of sublayers for fluid boundary layer and solid domain, respectively. In this offset stenosis problem, we only use one layer as solid to speed up the computation.The last flag -sch5 will scale the mesh from [mm] to [m].

An example of the log during the pre-processing is:

Click to expand log output

Failed to import probe.probe11
Error message: Couldn't find a file matching the module name: probe.probe11  (opt_in = False)
Neither oasis nor oasismove is installed. Exiting simulation..
Neither oasis nor oasismove is installed. Exiting simulation..
WARNING: OasisMove is not installed, running moving domain CFD is not available
WARNING: OasisMove is not installed, running moving domain simulations (MovingAtrium) is not available

— Working on case: offset_stenosis

— Load model file

— Surface overview: Total number of triangles: 14993. Total number of points: 7583.

— Check the surface. Found 0 NaN cells.

— Cleaning the surface. Done.

— Check the surface. Found 0 NaN cells.

— Get centerlines

Cleaning surface. Triangulating surface. Computing centerlines. Computing centerlines…

— No smoothing of surface

— Adding flow extensions

— Compute the model centerlines with flow extension.

Cleaning surface. Triangulating surface. Computing centerlines. Computing centerlines…— Computing distance to sphere

— Generating FSI mesh

Not capping surface Remeshing surface Iteration 1/10 Iteration 2/10 Iteration 3/10 Iteration 4/10 Iteration 5/10 Iteration 6/10 Iteration 7/10 Iteration 8/10 Iteration 9/10 Iteration 10/10 Final mesh improvement Computing projection Generating boundary layer fluid Generating boundary layer solid Capping inner surface Remeshing endcaps Iteration 1/10 Iteration 2/10 Iteration 3/10 Iteration 4/10 Iteration 5/10 Iteration 6/10 Iteration 7/10 Iteration 8/10 Iteration 9/10 Iteration 10/10 Final mesh improvement Computing sizing function Generating volume mesh TetGen command line options: pq1.414000q10.000000q165.000000YsT1.000000e-08zQm Assembling fluid mesh Assembling final FSI mesh — Writing Dolfin file — Converting XML mesh to HDF5

— Flattening the inlet/outlet if needed

Surface with ID 2 is not flat: Standard deviation of facet unitnormals is 0.031194747575524318, greater than threshold of 0.001 Moving nodes into a flat plane Surface with ID 3 is not flat: Standard deviation of facet unitnormals is 0.014826901671377794, greater than threshold of 0.001 Moving nodes into a flat plane Changes made to the mesh file — Evaluating edge length

=== Mesh information === X range: -9.53805 to 24.8625 (delta: 34.4006) Y range: -3.45556 to 3.4564 (delta: 6.9120) Z range: -3.45595 to 3.45649 (delta: 6.9124) Number of cells: 20829 Number of cells per processor: 20829 Number of edges: 0 Number of faces: 42581 Number of facets: 42581 Number of vertices: 3890 Volume: 1166.8263 Number of cells per volume: 17.8510 — Saving probes points in: offset_stenosis_probe_point.json

Your folder with input mesh should now look like:

Folder
├── commands_20250114132437.txt
├── offset_stenosis.h5
├── offset_stenosis.stl
├── offset_stenosis.vtu
├── offset_stenosis.xml.gz
├── offset_stenosis_boundaries.pvd
├── offset_stenosis_boundaries000000.vtu
├── offset_stenosis_domains.pvd
├── offset_stenosis_domains000000.vtu
├── offset_stenosis_edge_length.h5
├── offset_stenosis_edge_length.xdmf
├── offset_stenosis_info.json
└── offset_stenosis_probe_point.json

Finally, to generate solid probes, please run:

vasp-generate-solid-probe --mesh-path offset_stenosis.h5 --fsi-region -0.0002 0.016 -0.0035 0.0035 -0.0035 0.0035

Fig. 6 shows an example of the volumetric mesh.

_images/stenosis_mesh.png

Fig. 6 Volumetric mesh of offset stenosis with solid (red) and fluid (blue) regions.#

FSI simulations#

Note

An input mesh for FSI simulation, offset_stenosis.h5, can be downloaded here. Additionally, fluid and solid probe points are also required to run this problem and can be downloaded here and here.

The next step is to run FSI simulation using turtleFSI. First, make sure to move to the folder where offset_stenosis.py is located, i.e. src/vasp/simulations/. Then, one can simply run

turtleFSI -p offset_stenosis --mesh_path=/path/to/your_folder/offset_stenosis.h5 

where --mesh-path is the path to the volumetric mesh generated from the pre-processing. While this problem can be executed on a normal laptop, it may be quite slow. As such, it is highly recommended to speed up the computation by using MPI. For example, one can use eight processors as:

mpirun -np 8 turtleFSI -p offset_stenosis --mesh_path=/path/to/your_folder/offset_stenosis.h5 

to speed up the simulation.

During the FSI simulation, turtleFSI will print out some information that are useful for monitoring the progress. A simplified example of such a log is

Flow Properties:
  Flow Rate at Inlet: 1.9492469835105836e-06
  Velocity (mean, min, max): 0.05444848866112857, 4.11345811806542e-16, 0.49485295630442205
  CFL (mean, min, max): 0.2379617502537257, 1.797746305617944e-15, 0.9627060455857645
  Reynolds Numbers (mean, min, max): 226.8227703002397, 1.7135938733956218e-12, 1061.4698626220043
Solved for timestep 122, t = 0.1220 in 7.4 s
ramp_factor = 0.6767374218896292
Instantaneous normal stress prescribed at the FSI interface 9648.21114507385 Pa
Newton iteration 0: r (atol) = 3.712e-02 (tol = 1.000e-06), r (rel) = 1.988e-02 (tol = 1.000e-06) 
Newton iteration 1: r (atol) = 5.473e-05 (tol = 1.000e-06), r (rel) = 1.914e-02 (tol = 1.000e-06) 
Newton iteration 2: r (atol) = 4.711e-06 (tol = 1.000e-06), r (rel) = 1.943e-04 (tol = 1.000e-06) 
Probe Point 1: Velocity: (0.09891215934965708, 0.0003292221793639628, 0.009674117733206467) | Pressure: 93.38433393779773
Probe Point 1: Displacement: (-3.304118240759384e-05, 0.00018922478784845321, 0.00017120957053687062)

Results of the simulation are saved in a folder offset_stenosis_results with the following structure:

offset_stenosis_results/1
├── Checkpoint
│   ├── checkpoint_d1.h5
│   ├── checkpoint_d1.xdmf
│   ├── checkpoint_p1.h5
│   ├── checkpoint_p1.xdmf
│   ├── checkpoint_v1.h5
│   ├── checkpoint_v1.xdmf
│   └── default_variables.json
├── Mesh
│   └── mesh.h5
└── Visualization
    ├── displacement.h5
    ├── displacement.xdmf
    ├── pressure.h5
    ├── pressure.xdmf
    ├── velocity.h5
    └── velocity.xdmf

Post-processing#

Post-processing Mesh#

The first step of post-processing is to refine mesh as we used save_deg=2, but this can be skipped in case you run the simulation with save_deg=1. To refine the mesh, run:

vasp-refine-mesh --folder offset_stenosis_results/1

where an example log is:

Click to expand log output

--- Refined mesh saved to: offset_stenosis_results/1/Mesh/mesh_refined.h5 
--- Correcting node numbering in refined mesh
--- Node numbering is incorrect between the refined mesh and the output velocity.h5 file
--- Sorting node coordinates
x coordinate is not unique and sort based on x, y, and z coordinates
--- Correcting node numbering of the topology array in the refined mesh
--- Correcting node numbering of the boundary topology array in the refined mesh
--- Correcting boundary values in the refined mesh
--- Saving the corrected node numbering to the refined mesh
The node numbering in the refined mesh has been corrected to match the output velocity.h5 file

Note

At a later stage in the post-processing, we will use refined mesh to extract domain specific variables, such as fluid velocity, from velocity.h5. For that to work properly, we perform sorting of the node numbering as indicate in the log.

The next step is to separate the mesh into fluid and solid only meshes by executing:

vasp-separate-mesh --folder offset_stenosis_results/1

with an example log:

Click to expand log output

--- Separating fluid and solid domains using domain IDs 
--- Fluid domain ID: [1, 1001] and Solid domain ID: 2
--- Saving fluid domain to offset_stenosis_results/1/Mesh/mesh_fluid.h5
--- Saving solid domain to offset_stenosis_results/1/Mesh/mesh_solid.h5
--- Done separating domains
--- fluid topology does not need to be fixed
--- Saving the fixed mesh file
--- Fixing topology of solid domain
--- Saving the fixed mesh file
--- Refined mesh exists, separating domains for refined mesh
--- Saving fluid domain to offset_stenosis_results/1/Mesh/mesh_refined_fluid.h5
--- Saving solid domain to offset_stenosis_results/1/Mesh/mesh_refined_solid.h5
--- Done separating domains
--- Fixing topology of fluid domain
--- Saving the fixed mesh file
--- Fixing topology of solid domain
--- Saving the fixed mesh file

Post-processing FEniCS#

In this section, we will go through how to separate fluid velocity and solid displacement from the original visualization files. This procedure will enable efficient computation of hemodynamics and stress/strain at a later stage.

First, you need to create FEniCS-compatible files as original visualization files from turtleFSI can not be re-read by FEniCS. To do so, run:

vasp-create-hdf5 --folder offset_stenosis_results/1

which creates u.h5 and d_solid.h5 under newly created folder Visualization_separate_domain.

To visualize those separated variables, you can run

vasp-create-separate-domain-viz --folder offset_stenosis_results/1

Fig. 7 is an visualization of domain specific visualization.

_images/stenosis_separate_variables.png

Fig. 7 Fluid velocity (left) and solid displacement (right)#

Finally, hemodynamics and stress/strain can be computed, independently and simultaneously, as:

vasp-compute-hemo --folder offset_stenosis_results/1

and

vasp-compute-stress --folder offset_stenosis_results/1

Fig. 8 and Fig. 9, respectively, show computed hemodynamics and stress/strain.

_images/stenosis_hemo.png

Fig. 8 Hemodynamic indices computed based on fluid velocity#

_images/stenosis_stress.png

Fig. 9 Solid stress and strain computed based on solid displacement#

Post-processing h5py#

Attention

While this section comes after post-processing fenics, all the post-processing in this section can be performed as long as you have meshes from the post-processing mesh.

We first start by generating spectrograms, which represent the evolution of frequency over time. To do so, run:

vasp-create-spectrograms-chromagrams --folder offset_stenosis_results/1 -q d --component all --start-time 0.951 --end-time 1.902 --ylim 500

Here, -q indicates the quantity of interest, namely displacement (d), velocity (v), or pressure (p). The second flag --component specifies which directional component to use, where all means that we use all x, y, and z components to generate spectrograms. --start-time/end-time and --ylim are used to specify the time-window, i.e. x-axis, and maximum frequency, i.e. y-axis, of the resulting spectrograms. In this example, we focus on the second cardiac cycle (0.951 ~ 1.902 s) and up to 500 Hz. Fig. 10 is an example of the figure you get as a result. For detailed explanation of chromagram and spectral bandness index, please refer to MacDonald et al. [MNTS22]

_images/stenosis_spectrogram.png

Fig. 10 Displacement spectrogram (top), chromagram (middle), and spectral bandness index (SBI, bottom)#

Now, from the spectogram, it is quite clear that there are some concentration of the frequency contents, as evident from narrow frequency bands. Physically, those frequency are associated with the specific directional motion of the wall, called mode shapes. To extract and visualize such motions, you can use vasp-create-hi-pass-viz command. For example, two distinct modes (70 ~ 90 Hz and 150 ~ 170 Hz) can be extracted by running the following command:

vasp-create-hi-pass-viz --folder offset_stenosis_results/1 -q d --start-time 0.951 --end-time 1.902 --bands 70 90

and

vasp-create-hi-pass-viz --folder offset_stenosis_results/1 -q d --start-time 0.951 --end-time 1.902 --bands 150 170

Those two modes correspond to the specific motion of the stenosis wall, as shown in Fig. 11.

_images/stenosis_modes.png

Fig. 11 Mode shapes of stenosis with expansion/contraction (mode1) and left/right (mode2) motions#

[MNTS22]

Daniel E. MacDonald, Mehdi Najafi, Lucas Temor, and David A. Steinman. Spectral Bandedness in High-Fidelity Computational Fluid Dynamics Predicts Rupture Status in Intracranial Aneurysms. Journal of Biomechanical Engineering, 2022. doi:10.1115/1.4053403.

[VFF07]

Sonu S. Varghese, Steven H. Frankel, and Paul F. Fischer. Direct numerical simulation of stenotic flows. Part 1. Steady flow. Journal of Fluid Mechanics, 582:253–280, 2007. doi:10.1017/S0022112007005848.