Basics

In order to build any synthetic vascular network(s), you will need to define at least three components:

  • the perfusion domain in which the network(s) is built

  • seed point(s) where the vascular network(s) is initiated

  • the size of the vascular network(s)

By default if no seed points are given, vascular networks will be randomly seeded along the surface boundary of the perfusion domain. Likewise if no tree size is given, vascular networks will be initiated and dynamically grown. Therefore if the seed points and/or size of the vascular networks are unknown, the toolkit will make basic assumptions on the backend to fill in the missing information. The only component SVT cannot assume is the perfusion domain. This must be supplied by the user.

Defining a Perfusion Domain

In this section, we will cover the basics of creating a perfusion domain object necessary for generating a vascular network with SVT. The most basic method for defining a perfusion domain relies on point-wise data along the boundary. To create an example domain, we will use Pyvista to obtain boundary points and normals for a simple cube volume.

 1import pyvista as pv
 2import svcco
 3
 4# Define some set of points along a cube boundary
 5cube = pv.Cube().triangulate().subdivide(4)
 6
 7# Create a domain instance
 8surf = svcco.surface()
 9
10# Assign points and normals to the domain object
11surf.set_data(cube.points,cube.point_normals)
12
13# Solve the domain geometry to obtain an implicit representation
14surf.solve()
15
16# Build the implicit domain
17surf.build(q=3)

To visualize the perfusion domain object, we can call svcco.plot_volume(surf) to render some number of isocontours for the implicit funciton defining our domain. Below shows only one isocontour of the implicit cube domain near the boundary of the domain.

In this example, we provided the point-wise normal vectors associated with the cube since we already know the normals for our domain boundary. It is not always the case that normal information will be available. Therefore, point-wise normal vectors do not need to be provided by the user in order to create perfusion domain. However, providing them allows for fast solving of the implicit function defining the domain and can generally improve accuracy at the boundary if a domain has sharp edges.

Note

Providing normal vector information at points is not required for perfusion domain objects, but it can improve performance during the solve() and build() stages, especially if the domain is only C0 continuous.

We can demonstrate this this same process without including normal vector information below:

 1import pyvista as pv
 2import svcco
 3
 4# Define some set of points along a cube boundary
 5cube = pv.Cube().triangulate().subdivide(4)
 6
 7# Create a domain instance
 8surf = svcco.surface()
 9
10# Assign points and normals to the domain object
11surf.set_data(cube.points)
12
13# Solve the domain geometry to obtain an implicit representation
14surf.solve()
15
16# Build the implicit domain
17surf.build(q=3)

Of course, defining perfusion domains through point arrays can be cumbersome and not immediately available for many geometry files, especially STL files which are common for 3D fabrication. Often complex geometries are represented as mesh files of varying quality. To handle these types of files for perfusion domain creation, we can use the load() method to import and clean a mesh geometry file prior to implicit domain creation.

 1import svcco
 2
 3# Define a domain instance
 4surf = svcco.surface()
 5
 6# Use the load method to import a geometry
 7surf.load('/path/to/file')
 8
 9# Solve the domain geometry to obtain an implicit representation
10surf.solve()
11
12# Build the implicit domain
13surf.build(q=3)

Note

If the load() method accepts a string parameter for a file path to supported mesh files. If no arguments are provided, the method will open a file navigator window for the user to select a file through an easy-to-use interface.

For more information and advanced features of the perfusion domain svcco.surface() class, please refer to the documentation in implicit module.

Building a Vascular Tree within a Domain

After defining an appropriate perfusion domain object, users can build a synthetic vascular network. We will demonstrate a build of a simple vascular tree within the cube domain presented above. By default, the standard unit system for building synthetic vasculature is in centimeter-gram-second units (cgs). This will be important to remember when assigning physical parameters to vascular networks and for downstream applications.

 1import svcco
 2
 3# Define a tree instance
 4t = svcco.tree()
 5
 6# Assign a perfusion domain object
 7t.set_boundary(surf)
 8
 9# Set the root vessel for the tree, if no parameters are given this
10# root will be randomly placed on the boundary
11t.set_root()
12
13# Add some number of vessels to the tree
14t.n_add(100)
\[\begin{split}\min_x & f(x), x \in \mathbb{R}^n \\ \text{s.t.} & g_i(x) \ge 0, ~ \forall i = 1,\dots,m \\ & h_j(x) = 0, ~\forall j = 1,\dots,p\end{split}\]