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 regionconvexity_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 treedecay_probability
(float): Probability decay for space competitionnetwork_id
(list, optional): Specific networks to grow (default: all)progress
(bool): Show progress bar
Algorithm
For each vessel addition:
- Attempt vessel placement in each tree
- Check for collisions with all other trees
- Roll back changes if collision detected
- Retry until successful placement
- Update spatial indices and probabilities
Connection Methods
connect(*args, **kwargs)
Create connections between trees within each network.
Usage
forest.connect()
: start with linear connectionsforest.connect(3)
: start optimization with cubic curvesforest.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 treeplot_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
svv.tree.Tree
- Individual tree structuresvv.domain.Domain
- Spatial domains