svVascularize

Parameter Space & Sampling

Generate diverse vascular trees by exploring objective weights, constraints, sampling, and stochastic seeds.

Back to Docs

Overview

svVascularize exposes a set of parameters that control how trees grow, how candidates are proposed, which constraints are enforced, and how the local optimization balances competing criteria. By sweeping these parameters and varying random seeds, you can produce families of trees tailored to specific tissues, printing constraints, or hemodynamic goals.

Think of a run as \( \mathcal{T}(\theta, s) \): a tree produced from a parameter vector \( \theta \) and RNG seed \( s \). Exploring the space means scanning \( \theta \) (and replicates over \( s \)) to map design → outcome.

Key controls

CategoryParameters (examples)Effect
Domain & seeds geometry (mesh/SDF), inlets/outlets, root pose, initial terminals Where growth is allowed; initial topology & bias.
Demand & targets terminal count, volumetric demand field, sampling scheme (uniform / blue-noise / demand-weighted) Space-filling behavior; coverage uniformity; perfusion targeting.
Objective weights \(J = \alpha\,\text{Power} + \beta\,\text{Volume} + \gamma\,\text{Smoothness} + \dots\) Trade-off energy vs. material vs. geometry.
Murray / flow model exponent \( n \in [2.7,3.2] \), viscosity model, Poiseuille/0D options Radius scaling at bifurcations; shear/pressure profiles.
Hard constraints clearance, non-intersection, domain inclusion, degree limits, min length Feasibility filter; shapes admissible solutions.
Soft constraints angle bounds, taper/smoothness penalties, curvature limits Regularity of geometry; printability robustness.
Local optimization neighborhood size, line-search/trust-region, tolerance, max iters Quality/speed of bifurcation placement & sizing.
Stochasticity RNG seed, candidate pool size, tempered acceptance, resample policy Diversity and exploration vs. exploitation.

Objective & constraints (at a glance)

We typically minimize a penalized composite objective:

\[ J(\mathcal{T}) = \alpha \sum_i R_i Q_i^2 \;+\; \beta\,\text{Volume}(\mathcal{T}) \;+\; \gamma\,\text{Smooth}(\mathcal{T}) \;+\; \sum_k \lambda_k\,\phi_k(\mathcal{T}), \]

Here \(R_i\) are segment resistances (Poiseuille), \(Q_i\) flows from a fast 0D solve per candidate, and \(\phi_k\) are soft-constraint penalties; Murray-like scaling \(r_0^{\,n}=r_1^{\,n}+r_2^{\,n}\) is enforced exactly or with a strong penalty during local refinement. Hard constraints (clearance, non-intersection, domain inclusion) act as feasibility filters before scoring.

How to explore the space

A practical sweep workflow

  1. Define your domain, inlet/outlet sites, and a base parameter template \( \theta_0 \).
  2. Choose sweep axes (e.g., Murray exponent \(n\), terminal demand \(q_{\text{term}}\), angle penalty \(\gamma_{\angle}\)).
  3. Sample \( \Theta = \{\theta^{(1)},\ldots,\theta^{(M)}\} \) (grid, Sobol, etc.) and replicates \( s=1..R \).
  4. Generate trees \(\mathcal{T}(\theta^{(m)}, s)\); record metrics.
  5. Select candidates by constraints + objective, or compute a Pareto set for multi-objective trade-offs.

Illustrative sweep (pseudo-API)

This mirrors typical svVascularize usage; adjust names to your actual API.

import itertools, json, pathlib, numpy as np

# 1) Base parameters
base = dict(
    domain="heart_slice.sdf",                # or mesh
    inlets=[(12.0, 6.1, 4.7)],
    outlets=[],                              # optional; use forest-connection for loops
    terminals=1200,
    sampling="blue-noise",
    murray_n=3.0,
    weights=dict(alpha_power=1.0, beta_volume=0.1, gamma_smooth=0.05),
    constraints=dict(clearance=0.12, min_length=0.4, angle_min=25, angle_max=120),
    local_opt=dict(neighborhood=3.0, max_iters=20, tol=1e-3),
)

# 2) Define a sweep over two axes + RNG replicates
n_vals   = [2.8, 3.0, 3.2]
q_vals   = [0.8, 1.0, 1.2]                  # relative terminal demand scale (example)
seeds    = [11, 12, 13]

def paramsets():
    for n, q in itertools.product(n_vals, q_vals):
        p = json.loads(json.dumps(base))    # deep-ish copy
        p["murray_n"] = n
        p["demand_scale"] = q
        yield p

# 3) Run
outdir = pathlib.Path("runs/param_sweep")
outdir.mkdir(parents=True, exist_ok=True)

for i, theta in enumerate(paramsets()):
    for s in seeds:
        theta["seed"] = s
        # v = svv.Vascularizer(theta)               # construct
        # T = v.grow()                               # generate tree
        # metrics = v.metrics()                      # coverage, collisions, power, volume, etc.
        # v.save(outdir / f"tree_{i}_seed{s}.vtp")   # geometry
        # json.dump(metrics, open(outdir / f"tree_{i}_seed{s}.json","w"), indent=2)
        pass  # replace with real calls

# 4) Post-process: aggregate metrics and rank / build Pareto set

What to measure

MetricWhy it matters
Coverage (%) / voidsUniform perfusion and space-filling behavior.
Collisions / clearance violationsFeasibility and printability.
Total power & volumeEnergy vs. material trade-off in \(J\).
Radius & length distributionsPhysiological realism / manufacturability.
Bifurcation angles / taperGeometric regularity and flow quality.
Shear & pressure rangesHemodynamic safety windows.
Connectivity / loops (forest)Closed-circuit perfusion for tissues.

Starting ranges (tunable)

Over-tight constraints can stall growth. Use progressive tightening (anneal clearance/angles) or multi-resolution domains.

Reproducibility checklist

Next steps