---
type: Notebook
title: Volume and Mass-Dimension Predictions
description: >-
  Supports the Part II Methods sections on volume and mass-dimension
  predictions: SymPy walks Eqs. 11 to 18, reproduces the projected branch-volume
  mass dimension d = 3/2, and reconciles the D = 3, d = 3/2 and 4/3 values that
  appear across the theory.
resource: https://tyson-swetnam.github.io/fractal-notebooks/notebooks/metabolic-scaling/methods/3_volume_mass_dimension/
tags: [notebook, part-ii, metabolic-scaling, dimension-theory, self-affinity, jupyter]
companion_page: https://tyson-swetnam.github.io/fractal-notebooks/metabolic-scaling/methods/
notebook:
  source: https://github.com/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb
  raw: https://raw.githubusercontent.com/tyson-swetnam/fractal-notebooks/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb
  nbviewer: https://nbviewer.org/github/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb
  colab: https://colab.research.google.com/github/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb
  kernel: Python 3 (ipykernel)
  environment: Python 3 with sympy; imports docs/notebooks/metabolic-scaling/review/fractal_review_utils.py
  cells:
    markdown: 5
    code: 6
    executed: 6
    figures: 0
    errors: 0
  outputs: stored
sources:
  - id: ipynb
    resource: https://github.com/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb
    title: 3_volume_mass_dimension.ipynb
    last_modified: "2026-10-24T00:00:00Z"
generated:
  by: process:scripts/render_notebooks.py
  at: "2026-10-24T00:00:00Z"
status: stable
---

<!-- Generated by scripts/render_notebooks.py from the notebook named in the frontmatter. Do not edit: edit the .ipynb and re-run the script. -->

# Volume and Mass-Dimension Predictions

!!! info "Jupyter notebook"

    This page was rendered from the stored outputs of `3_volume_mass_dimension.ipynb` as of 2026-10-24; it is not executed at build time.

    [View on GitHub](https://github.com/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb) · [Open in nbviewer](https://nbviewer.org/github/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb) · [Open in Colab](https://colab.research.google.com/github/tyson-swetnam/fractal-notebooks/blob/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb) · [Raw .ipynb](https://raw.githubusercontent.com/tyson-swetnam/fractal-notebooks/main/docs/notebooks/metabolic-scaling/methods/3_volume_mass_dimension.ipynb)

    Kernel: Python 3 (ipykernel) · Environment: Python 3 with sympy; imports docs/notebooks/metabolic-scaling/review/fractal_review_utils.py · Companion page: [metabolic-scaling/methods/](https://tyson-swetnam.github.io/fractal-notebooks/metabolic-scaling/methods/)

**Part II: Metabolic Scaling Theory and Biological Fractals**

---

## Overview

This notebook supports the Methods sections *"Volume and Mass Dimension Predictions"* and *"Differential Box-Counting Methodology"*. Using **SymPy** it walks the volume relations (Eqs. 11–13), the projected-area / box-counting mass-dimension argument (Eqs. 14–18), and reproduces the manuscript's key result that the projected branch volume yields a fractal (mass) dimension $d = 3/2$. It then **reconciles** the three dimension values that appear across the theory — $D=3$, $d=3/2$, and $4/3$ — stating carefully which object each describes and why they differ.

Symbolic steps are tagged `[COMPUTED]` (SymPy-checked algebra) or `[ASSERTED]` (a modelling step / scaling ansatz taken from the paper that is not a pure algebraic identity).


## 1. Volume relations (Eqs. 11–13) `[COMPUTED / ASSERTED]`

The branching-network volume (Eq. 11):

$$ V_B = \pi \sum_{k=0}^{N} n_k\, r_k^2\, l_k. $$

Using the branching ratios $r_k = r_0\,\xi^k$ (with $\xi=n^{-1/2}$) and $l_k = l_0\,\gamma^k$ (with $\gamma=n^{-1/3}$), and $n_k = n^{k}$ branches at level $k$, we can evaluate the per-level term symbolically. The **space-filling** result is that the occupied volume is preserved across levels:

$$ v_n \propto l_n^3, \qquad V_{\text{net}} = n_k v_k \propto n_k\, l_k^3 \qquad\text{(Eqs. 12–13)}. $$

Let SymPy check that, with $n_k=n^k$ and $l_k = l_0\,n^{-k/3}$, the quantity $n_k l_k^3$ is **independent of $k$** — i.e. volume is conserved level to level (this is the space-filling constraint made explicit).


```python
import sympy as sp

n, k, r0, l0, pi = sp.symbols('n k r_0 l_0 pi', positive=True)

xi    = n**sp.Rational(-1,2)     # radius ratio
gamma = n**sp.Rational(-1,3)     # length ratio

r_k = r0 * xi**k                 # radius at level k
l_k = l0 * gamma**k              # length at level k
n_k = n**k                       # number of branches at level k

# --- Eq. 13: occupied network volume per level, V_net ~ n_k * l_k^3 ---
V_net_k = sp.simplify(n_k * l_k**3)
print("V_net(k)  = n_k * l_k^3        =", V_net_k)
print("simplified                     =", sp.powsimp(V_net_k, force=True))

# is it independent of k?  differentiate the exponent of n w.r.t. k
V_net_ratio = sp.simplify(V_net_k.subs(k, k+1) / V_net_k)
print("\nV_net(k+1)/V_net(k)            =", V_net_ratio)
assert sp.simplify(V_net_ratio - 1) == 0
print("-> = 1 exactly: occupied volume is CONSERVED across branching levels")
print("   (space-filling constraint, Eq. 13). [COMPUTED by SymPy]")

```

```text
V_net(k)  = n_k * l_k^3        = l_0**3
simplified                     = l_0**3

V_net(k+1)/V_net(k)            = 1
-> = 1 exactly: occupied volume is CONSERVED across branching levels
   (space-filling constraint, Eq. 13). [COMPUTED by SymPy]
```


```python
# --- Eq. 11 per-level term of V_B = pi * sum n_k r_k^2 l_k ---
term = sp.simplify(n_k * r_k**2 * l_k)
print("per-level term  n_k r_k^2 l_k  =", sp.powsimp(term, force=True))

# ratio between successive levels: reveals the geometric decay factor gamma*xi^2
ratio = sp.simplify(term.subs(k, k+1)/term)
ratio = sp.powsimp(ratio, force=True)
print("term(k+1)/term(k)              =", ratio, " (= n * xi^2 * gamma = gamma, since n*xi^2=1)")
print()
# The manuscript's Eq. 11 approximation V_B ~ gamma*xi^2 * V_N * (1 - n^-4/3):
# here the per-level branch-volume shrinks by gamma each level (a convergent
# geometric series), while the *occupied* volume V_net is conserved. [ASSERTED model]
gamma_val = sp.nsimplify(gamma)
print("common ratio of the branch-volume series = gamma = n^(-1/3) < 1")
print("=> V_B is a CONVERGENT geometric series (branch material is a vanishing")
print("   fraction of occupied space at deep levels). [COMPUTED]")

```

```text
per-level term  n_k r_k^2 l_k  = l_0*r_0**2/n**(k/3)
term(k+1)/term(k)              = n**(-1/3)  (= n * xi^2 * gamma = gamma, since n*xi^2=1)

common ratio of the branch-volume series = gamma = n^(-1/3) < 1
=> V_B is a CONVERGENT geometric series (branch material is a vanishing
   fraction of occupied space at deep levels). [COMPUTED]
```

## 2. Projected-area / box-counting argument (Eqs. 14–18) `[COMPUTED / ASSERTED]`

Viewing the 3-D network perpendicular to its principal axis projects it onto a 2-D image. The manuscript's chain:

$$ A \propto n^{2/3} l_N^{2} \quad\text{(Eq. 15)}, \qquad
N(\varepsilon) \propto \frac{A}{\varepsilon^2} \quad\text{(Eq. 17)}, \qquad
N(\varepsilon) \propto \varepsilon^{3/2} \quad\text{(Eq. 18)} $$

yielding a **mass dimension** $d = 3/2$ for the projected branch volume.

We check the exponent bookkeeping symbolically. The box-count scaling exponent (the fractal/mass dimension) is defined by $N(\varepsilon) \propto \varepsilon^{-d}$. The manuscript writes the projected-volume scaling as $N(\varepsilon)\propto\varepsilon^{3/2}$ in the sense that the *measured mass* accumulates as $\varepsilon^{3/2}$ under coarse-graining, giving $d = 3/2$. We reproduce the $3/2$ from the self-affine ansatz below.


```python
eps, L, lN, rN, d = sp.symbols('varepsilon L l_N r_N d', positive=True)

# Eq. 16 as written: A ~ V_B^(1/2) * l_N^(3/2) * r_N,  with V_B ~ L^3 (Eq. 14, non-fractal)
V_B = L**3
A_eq16 = sp.sqrt(V_B) * lN**sp.Rational(3,2) * rN
print("Eq.16  A ~ V_B^(1/2) l_N^(3/2) r_N, with V_B ~ L^3:")
print("       A ~", sp.powsimp(A_eq16, force=True), " => L^(3/2) l_N^(3/2) r_N")

# exponent of L via logarithmic derivative:  n = L * d(A)/dL / A  (handles fractional powers)
expo_L = sp.simplify(L * sp.diff(A_eq16, L) / A_eq16)
print(f"\nexponent of L in the projected area = {expo_L} = {sp.nsimplify(expo_L)}")
assert expo_L == sp.Rational(3,2)
print("This 3/2 is the self-affine mass-dimension the manuscript reports as d. [COMPUTED bookkeeping]")

```

```text
Eq.16  A ~ V_B^(1/2) l_N^(3/2) r_N, with V_B ~ L^3:
       A ~ L**(3/2)*l_N**(3/2)*r_N  => L^(3/2) l_N^(3/2) r_N

exponent of L in the projected area = 3/2 = 3/2
This 3/2 is the self-affine mass-dimension the manuscript reports as d. [COMPUTED bookkeeping]
```


```python
# --- Reproduce d = 3/2 from the self-affine (fBm) ansatz, the cleanest derivation ---
# For a self-affine trace/graph, the mass measured in a box of size eps scales as
#   M(eps) ~ eps^d,  with the affine (Hurst-type) exponent giving d = 3/2
# for the projected branch VOLUME. Combining the branching ratios (r ~ n^(-k/2),
# l ~ n^(-k/3)) gives radius ~ length^(3/2), which is the exponent computed below.
#
# Self-affine relation: radius scales as length^(a/b) = length^((1/2)/(1/3)) = length^(3/2)
a_exp, b_exp = sp.Rational(1,2), sp.Rational(1,3)
self_affine_exp = a_exp / b_exp
print(f"radius-vs-length self-affine exponent = a/b = (1/2)/(1/3) = {self_affine_exp}")
print(f"                                                            = {float(self_affine_exp)}")

d_val = self_affine_exp     # = 3/2
print(f"\n=> projected branch-volume mass dimension  d = {d_val} = {float(d_val)}")
assert d_val == sp.Rational(3,2)
print("   d = 3/2 = 1.5  reproduced. [COMPUTED by SymPy]")

```

```text
radius-vs-length self-affine exponent = a/b = (1/2)/(1/3) = 3/2
                                                            = 1.5

=> projected branch-volume mass dimension  d = 3/2 = 1.5
   d = 3/2 = 1.5  reproduced. [COMPUTED by SymPy]
```

## 3. Reconciling $D=3$, $d=3/2$, and $4/3$ `[the crux — stated carefully]`

Three different numbers appear across the theory. They are **not** contradictory once you track *which object* is being measured and *in which embedding*. This is the crux of the manuscript's argument and is stated here honestly, flagging which parts are computed and which are modelling assertions.

| Value | Object measured | Embedding / range | Where it comes from | Status |
|------:|-----------------|-------------------|---------------------|--------|
| $D=3$ | the full 3-D branching network / exchange surface | Hausdorff dim of a 3-D object, range $[2,3]$ | space-filling constraint $n\,l_k^3=\text{const}\Rightarrow D=\ln n/\ln(1/\gamma)=3$ | `[COMPUTED]` (notebook 2) |
| $d=3/2$ | the **projected branch volume** (2-D silhouette of the network) | self-affine mass dim; binary box-count range $[1,2]$ | Eqs. 15–18 / fBm ansatz $\alpha=3/2$ | `[COMPUTED]` bookkeeping (above) |
| $4/3$ | the *same projected image*, under a different scaling assumption | binary box-count range $[1,2]$ | Results §: "$4/3$ rather than $3/2$" from "Eq. 18" — **source equation not shown in Methods** | `[ASSERTED, unverified]` |

**Why they differ.** $D=3$ describes the whole 3-D network filling a volume; it is a *space-filling* dimension. $d=3/2$ describes the 2-D *projection* of that network's branch volume, and is the *self-affine* (fBm-type) mass dimension the manuscript compares its measurements to. The two are different because projecting a 3-D self-affine object to 2-D and measuring accumulated mass is a different operation from measuring the Hausdorff dimension of the 3-D set.

**The $4/3$ discrepancy is real and we do not paper over it.** The manuscript's Results section states that "from Equation 18" a differential mass dimension is "expected to equal $4/3$ rather than $3/2$", but the Methods as written derive $3/2$ and the equation producing $4/3$ is not shown (it is deferred to Supplementary Information). We therefore record $4/3$ as an **asserted, unverified** target, not a computed one.


```python
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.getcwd(), "..", "review")))  # review/ helpers, relative to this notebook
import fractal_review_utils as fru

print("The three competing theoretical targets, stated explicitly")
print("(from fractal_review_utils.THEORY_TARGETS, the review apparatus):\n")
fru.print_theory_targets()

```

```text
The three competing theoretical targets, stated explicitly
(from fractal_review_utils.THEORY_TARGETS, the review apparatus):

Competing theoretical targets the paper compares data against:

  D=3 (space-filling network)      value=3.0000
      object : the full 3-D branching network / exchange surface
      from   : WBE space-filling constraint N_k * l_k^3 = const  =>  D_H = ln n / ln(1/gamma) = 3
      range  : Hausdorff dim of the 3-D object, range 2..3

  D=3/2 (projected silhouette)     value=1.5000
      object : binary silhouette of the network projected to 2-D
      from   : Methods eqns 15-17 as written: N(eps) ~ eps^(3/2) for a 2-D projection
      range  : binary box-count, range 1..2

  D=4/3 (Results, eqn '18')        value=1.3333
      object : same projected image (contradicts the 3/2 target)
      from   : Results section: 'a differential mass dimension for such an image is expected to equal 4/3 rather than 3/2' -- source eqn not shown
      range  : binary box-count, range 1..2
```


```python
# Numerically pin the three values and their relationships
D_network = sp.Rational(3,1)
d_proj    = sp.Rational(3,2)
d_alt     = sp.Rational(4,3)

print(f"D (space-filling 3-D network)      = {D_network} = {float(D_network):.4f}")
print(f"d (self-affine projected mass dim) = {d_proj} = {float(d_proj):.4f}")
print(f"4/3 (Results 'Eq.18', unverified)  = {d_alt} = {float(d_alt):.4f}")
print()
print(f"Note: d = 3/2 sits in the binary box-count range [1,2]  -> {1 <= float(d_proj) <= 2}")
print(f"      4/3        sits in the binary box-count range [1,2]  -> {1 <= float(d_alt) <= 2}")
print(f"      D = 3      sits in the 3-D Hausdorff range [2,3]     -> {2 <= float(D_network) <= 3}")
print()
print("So 3/2 and 4/3 are comparable (both 2-D silhouette measures); D=3 is a")
print("different embedding entirely. The 3/2-vs-4/3 gap is an internal")
print("inconsistency in the source text, recorded here, not resolved by us.")

```

```text
D (space-filling 3-D network)      = 3 = 3.0000
d (self-affine projected mass dim) = 3/2 = 1.5000
4/3 (Results 'Eq.18', unverified)  = 4/3 = 1.3333

Note: d = 3/2 sits in the binary box-count range [1,2]  -> True
      4/3        sits in the binary box-count range [1,2]  -> True
      D = 3      sits in the 3-D Hausdorff range [2,3]     -> True

So 3/2 and 4/3 are comparable (both 2-D silhouette measures); D=3 is a
different embedding entirely. The 3/2-vs-4/3 gap is an internal
inconsistency in the source text, recorded here, not resolved by us.
```

## Summary

**Verified / computed with SymPy** `[COMPUTED]`:

- Occupied network volume $V_{\text{net}}=n_k l_k^3$ is **conserved across branching levels** (ratio $=1$ exactly) under $l_k=l_0 n^{-k/3}$ — the space-filling constraint (Eqs. 12–13).
- The branch-material series $V_B=\pi\sum n_k r_k^2 l_k$ decays by a common ratio $\gamma=n^{-1/3}<1$ per level (convergent geometric series), consistent with Eq. 11.
- The projected area carries an $L^{3/2}$ dependence (Eq. 16), and the self-affine ansatz $\text{radius}\propto\text{length}^{(1/2)/(1/3)}$ gives the mass dimension $d = 3/2 = 1.5$ (Eq. 18).

**Reconciliation (the crux):**

- $D=3$ = space-filling Hausdorff dimension of the full 3-D network (range $[2,3]$).
- $d=3/2$ = self-affine mass dimension of the 2-D *projection* of the branch volume (range $[1,2]$); the fBm prediction $\alpha=3/2$ the manuscript compares data against.
- $4/3$ = an alternative target the Results section attributes to "Eq. 18" for a different image type; the producing equation is **not shown** in the Methods.

**Caveats / honesty flags:**

- The $d=3/2$ result rests on a self-affine scaling *ansatz* (a modelling assumption from WBE/fBm), not a pure algebraic identity — the exponent bookkeeping is machine-checked, but the ansatz itself is asserted.
- The $4/3$ value is recorded as **unverified**: it cannot be reproduced from the Methods equations as written, and we do not fabricate the missing derivation. The $3/2$-vs-$4/3$ inconsistency is a genuine gap in the source text.
