svVascularize

svv.forest.Forest

Core Class v1.0.0

The Forest class defines collections of synthetic vascular tree structures that are used to abstract the physical representations of generated interpenetrating vascular networks.

Forests enable the generation of multiple interconnected vascular networks within the same domain, with collision detection between networks, competitive growth patterns, and various connection strategies between trees. This is essential for modeling complex multi-network vascular systems like arterial-venous networks or competing tumor vasculature.

Quick Example

from svv.forest import Forest
from svv.domain import Domain
import numpy as np
import pyvista as pv

# Create domain
mesh = pv.read('tissue_region.stl')
domain = Domain(mesh)
domain.create()
domain.solve()
domain.build()

# Initialize forest with 2 networks, 2 trees each
forest = Forest(
    domain=domain,
    n_networks=2,
    n_trees_per_network=[2, 2],
    physical_clearance=0.5,  # mm spacing between vessels
    compete=True  # Enable competition for space
)

# Set domain for all trees
forest.set_domain(domain)

# Define start points for each tree
start_points = [
    [np.array([0, 0, -10]), np.array([0, 0, 10])],  # Network 1
    [np.array([-10, 0, 0]), np.array([10, 0, 0])]   # Network 2
]
forest.set_roots(start_points)

# Grow networks
forest.add(100)  # Add 100 vessels to each tree

# Connect trees within networks (start with cubic curves)
forest.connect(3)

# Visualize
forest.show(plot_domain=True)

Constructor

Forest(**kwargs)

Parameters

Parameter Type Default Description
domain Domain None Spatial domain for tree generation
n_networks int 1 Number of vascular networks
n_trees_per_network list[int] [2] Number of trees in each network
start_points list[np.ndarray] None Starting points for each tree
directions list[np.ndarray] None Initial directions for each tree
physical_clearance float 0.0 Minimum spacing between vessels (mm)
compete bool False Enable competition for space between networks

Attributes

Attribute Type Description
networks list[list[Tree]] 2D list of Tree objects organized by network
connections ForestConnection Connection manager for inter-tree links
domain Domain Spatial domain constraining all trees
n_networks int Number of separate networks
n_trees_per_network list[int] Tree count for each network
geodesic callable Geodesic path constructor for connections
convex bool Whether domain is convex
physical_clearance float Inter-vessel spacing constraint
compete bool Competition mode flag

Setup Methods

set_domain(domain, convexity_tolerance=1e-2)

Set the spatial domain for all trees in the forest.

Parameters

  • domain (Domain): Domain object defining growth region
  • convexity_tolerance (float): Tolerance for convexity determination

Notes

Automatically initializes geodesic path constructor and propagates domain to all trees.

set_roots(*args, **kwargs)

Set root vessels for all trees with collision detection.

Parameters

  • start_points (list): 2D list of starting points [network][tree]
  • directions (list, optional): 2D list of initial directions

Algorithm

Iteratively places roots while checking for collisions between all previously placed roots. Retries placement if collision is detected.

Growth Methods

add(n_vessels=1, decay_probability=0.9, network_id=None, **kwargs)

Add vessels to trees with inter-network collision detection.

Parameters

  • n_vessels (int): Number of vessels to add to each tree
  • decay_probability (float): Probability decay for space competition
  • network_id (list, optional): Specific networks to grow (default: all)
  • progress (bool): Show progress bar

Algorithm

For each vessel addition:

  1. Attempt vessel placement in each tree
  2. Check for collisions with all other trees
  3. Roll back changes if collision detected
  4. Retry until successful placement
  5. Update spatial indices and probabilities

Connection Methods

connect(*args, **kwargs)

Create connections between trees within each network.

Usage

  • forest.connect(): start with linear connections
  • forest.connect(3): start optimization with cubic curves
  • forest.connect(curve_type='NURBS'): switch curve family

Notes

The method instantiates a ForestConnection that orchestrates TreeConnection and VesselConnection objects to build collision-free connecting vessels.

Export Methods

export_solid(outdir=None, shell_thickness=0.0)

Export all trees as 3D solid models.

Parameters

  • outdir (str, optional): Output directory (default: '3d_tmp')
  • shell_thickness (float): Wall thickness for hollow vessels

Notes

Exports each tree individually. Files are named by network and tree indices.

show(**kwargs)

Visualize the entire forest with PyVista.

Parameters

  • colors (list[str], optional): Cycle of colors for each tree
  • plot_domain (bool): Overlay the domain boundary (default: False)
  • return_plotter (bool): Return the PyVista plotter instead of showing
  • **kwargs: Additional PyVista plotting parameters

Examples

Arterial-Venous Network

from svv.forest import Forest
from svv.domain import Domain
import numpy as np

# Create organ domain
organ = pv.read('liver.stl')
domain = Domain(organ)
domain.create()
domain.solve()
domain.build(resolution=40)

# Create forest with arterial and venous networks
forest = Forest(
    domain=domain,
    n_networks=2,  # Arterial and venous
    n_trees_per_network=[1, 1],
    physical_clearance=1.0  # 1mm minimum spacing
)

# Set inlet/outlet points
arterial_inlet = np.array([0, 0, -50])
venous_outlet = np.array([0, 0, 50])

forest.set_roots(
    start_points=[[arterial_inlet], [venous_outlet]],
    directions=[[np.array([0, 0, 1])], [np.array([0, 0, -1])]]
)

# Grow networks with competition
for i in range(200):
    forest.add(1)
    if i % 50 == 0:
        print(f"Step {i}: Arterial terminals: {forest.networks[0][0].n_terminals}, "
              f"Venous terminals: {forest.networks[1][0].n_terminals}")

# Connect within networks if multiple trees
forest.connect(3)

# Visualize with different colors
forest.show(colors=['red', 'blue'], plot_domain=True)

Multi-Tree Network with Connections

# Create forest with multiple trees per network
forest = Forest(
    n_networks=1,
    n_trees_per_network=[4],  # 4 trees in single network
    physical_clearance=0.5
)

# Set domain
forest.set_domain(domain)

# Place trees at corners
corners = [
    np.array([-10, -10, 0]),
    np.array([10, -10, 0]),
    np.array([10, 10, 0]),
    np.array([-10, 10, 0])
]

forest.set_roots(start_points=[corners])

# Grow trees
forest.add(50)

# Create inter-tree connections
forest.connect(5)  # Higher degree for smoother connections

# Access connection data
if forest.connections:
    print(f"Tree connections solved: {len(forest.connections.tree_connections)}")

# Export connected network
forest.export_solid(outdir='connected_network')

Competitive Growth Pattern

# Enable competition between networks
import numpy as np
forest = Forest(
    domain=domain,
    n_networks=3,  # Three competing networks
    n_trees_per_network=[1, 1, 1],
    physical_clearance=0.1,
    compete=True  # Enable competition
)

# Set starting points
starts = [
    [np.array([0, -20, 0])],    # Network 1
    [np.array([17, 10, 0])],    # Network 2
    [np.array([-17, 10, 0])]    # Network 3
]

forest.set_roots(start_points=starts)

forest.add(50)

# Analyze competition results
import matplotlib.pyplot as plt
# Track terminal counts over time
terminal_history = []
for _ in range(50):
    forest.add(1)
    terminal_history.append([forest.networks[i][0].n_terminals for i in range(3)])

terminal_history = np.array(terminal_history)
for i in range(terminal_history.shape[1]):
    plt.plot(terminal_history[:, i], label=f'Network {i}')
plt.xlabel('Growth Step')
plt.ylabel('Number of Terminals')
plt.legend()
plt.title('Competitive Growth Dynamics')
plt.show()

Algorithms

Collision Detection: The Forest class implements comprehensive collision detection between all trees during growth. Each new vessel is checked against all existing vessels in other trees, with automatic rollback on collision detection.

Space Sampling: When sampling space for new terminal selection the probability that a region which has been previously sampled is drawn again for another terminal decays by the indicated value to encourage the vascular growth into unvisited territories.

Performance: Growth complexity is O(n²m) where n is the number of trees and m is vessels per tree. For large forests, consider growing networks sequentially or using parallel processing for collision checks.

Connection Algorithms

The forest supports multiple connection strategies:

  • Bezier: Smooth polynomial curves of specified degree
  • NURBS: Non-uniform rational B-splines for precise control
  • Geodesic: Shortest paths along the domain surface
  • Catmull-Rom: Interpolating splines through control points

See Also