Morphology#

class braincell.Morphology(*, root_name, root_branch)[source]#

Mutable morphology tree for authoring, querying, and visualization.

Morpho is the central entry point for building neuron morphologies. It owns a tree of MorphoBranch nodes, each wrapping an immutable Branch geometry. Children are attached via attach(), attribute assignment on a MorphoBranch, or

the branch[parent_x].child_name = branch syntax sugar.

Prefer the factory classmethods from_root(), from_swc(), and from_asc() over the raw constructor.

Parameters:
  • root_name (str | None) – Name of the root branch. If None, a name is auto-generated from the branch type (e.g., "soma_0").

  • root_branch (Branch) – Geometry of the root branch.

Raises:

ValueError – If root_name is a reserved name or already taken, or is not a valid Python identifier.

See also

Branch

Immutable segment geometry.

MorphoBranch

Tree-local branch node view.

Notes

Branch names must be valid Python identifiers (no leading underscore) and must not collide with reserved method or metric names. Auto-naming follows the pattern "{type}_{n}" (e.g., "dend_0", "axon_1").

Whole-morphology metrics (total_length, n_branches, etc.) are exposed directly on the Morpho instance.

Examples

Build a simple morphology by hand:

>>> import brainunit as u
>>> from braincell import Branch, Morphology
>>> soma = Branch.from_lengths(
...     lengths=[20.0] * u.um,
...     radii=[10.0, 10.0] * u.um,
...     type="soma",
... )
>>> dend = Branch.from_lengths(
...     lengths=[50.0, 40.0] * u.um,
...     radii=[2.0, 1.5, 1.0] * u.um,
...     type="dendrite",
... )
>>> morph = Morphology.from_root(soma, name="soma")
>>> morph.soma.dend = dend
>>> len(morph)
2
>>> morph.n_branches
2
>>> morph.total_length
110.0 * umetre

Load from an SWC file:

>>> morph = Morphology.from_swc("neuron.swc")
attach(*, parent, child_branch, child_name=None, parent_x=1.0, child_x=0.0)[source]#

Attach a child branch to a parent in this morphology.

Parameters:
  • parent (str | MorphoBranch) – Parent branch, specified by name or node reference.

  • child_branch (Branch) – Geometry of the child branch to attach.

  • child_name (str | None) – Name for the child branch. If None, auto-generated from the branch type (e.g., "dend_0").

  • parent_x (float) – Attachment point on the parent branch: 0 (proximal), 0.5 (midpoint, soma only), or 1 (distal, default).

  • child_x (float) – Attachment point on the child branch: 0 (proximal, default) or 1 (distal).

Returns:

The newly attached child branch node.

Return type:

MorphoBranch

Raises:
  • ValueError – If parent_x or child_x is invalid, if parent_x is 0.5 on a non-soma parent, or if child_name already exists.

  • TypeError – If parent is not a string or MorphoBranch.

  • KeyError – If parent is a string that does not match any branch name.

Notes

parent_x=0 attaches to the proximal end of the parent branch. This is typically used when the parent is itself a child branch and you want to attach at its connection point rather than its distal end.

Examples

>>> import brainunit as u
>>> from braincell import Branch, Morphology
>>> soma = Branch.from_lengths(
...     lengths=[20.0] * u.um,
...     radii=[10.0, 10.0] * u.um,
...     type="soma",
... )
>>> dend = Branch.from_lengths(
...     lengths=[50.0] * u.um,
...     radii=[2.0, 1.0] * u.um,
...     type="dendrite",
... )
>>> morph = Morphology.from_root(soma, name="soma")
>>> child = morph.attach(
...     parent="soma",
...     child_branch=dend,
...     child_name="apical",
... )
>>> child.name
'apical'
branch(*, name=None, index=None, order=None)[source]#

Retrieve a single branch by name or index.

Parameters:
  • name (str | None) – Branch name. Mutually exclusive with index.

  • index (int | None) – Branch index in the specified order. Mutually exclusive with name.

  • order (str | None) – Ordering strategy when querying by index: "default", "type", or "depth". Ignored when querying by name.

Returns:

The requested branch.

Return type:

MorphoBranch

Raises:
  • TypeError – If neither or both of name and index are provided, or if order is given with name.

  • KeyError – If name does not exist.

  • IndexError – If index is out of range.

branch_by_order(*, order='default')[source]#

Query branches in a specific order.

Parameters:

order (str) – Ordering strategy: "default" (by node ID), "type" (by SWC type rank then name), or "depth" (by depth then index).

Returns:

Ordered branches.

Return type:

tuple[MorphoBranch, ...]

Raises:

ValueError – If order is not recognized.

property branches: tuple[MorphoBranch, ...]#

All branches in default order (by node ID).

Returns:

All branches in the tree.

Return type:

tuple of MorphoBranch

See also

branch_by_order

Query branches in a specific order.

property diam_arc_mean: Quantity#

Return the morphology-wide arc-length-weighted mean diameter.

property edges: tuple[MorphoEdge, ...]#

All directed edges in the morphology tree.

Returns:

Parent-child connections, excluding the root (which has no parent).

Return type:

tuple of MorphoEdge

classmethod from_asc(path, *, return_report=False)[source]#

Load a morphology from a Neurolucida ASC file.

Delegates to AscReader for parsing and validation.

Parameters:
  • path (str or Path) – Path to the ASC file.

  • return_report (bool) – If True, return a (Morpho, AscReport) tuple instead of just the morphology.

Returns:

The loaded morphology, optionally with a diagnostic report.

Return type:

Morphology or (Morphology, AscReport)

See also

from_swc

Load from SWC format.

Examples

>>> from braincell import Morphology
>>> morph = Morphology.from_asc("neuron.asc")
classmethod from_neuromorpho(neuron_id, *, cache_dir=None, mode='neuromorpho', client=None, return_report=False, overwrite=False)[source]#

Fetch a NeuroMorpho.Org neuron and return it as a Morphology.

Downloads the standardized CNG SWC into cache_dir (defaulting to ~/.cache/braincell/neuromorpho) and parses it with from_swc() using mode="neuromorpho". Already-cached neurons are not re-downloaded unless overwrite=True.

Parameters:
  • neuron_id (int) – NeuroMorpho.Org numeric identifier (e.g. 10047).

  • cache_dir (str or Path or None) – Cache root. None resolves to ~/.cache/braincell/neuromorpho.

  • mode (str or None) – SWC import mode forwarded to from_swc(). Defaults to "neuromorpho" (copies soma attachment points into child branches).

  • client (NeuroMorphoClient or None) – Optional pre-configured client (custom session, retries, etc.). A transient client is created when None.

  • return_report (bool) – If True, also return the SwcReport from parsing.

  • overwrite (bool) – If True, re-download even when the file is already cached.

Returns:

The parsed morphology, optionally with a diagnostic report.

Return type:

Morphology or (Morphology, SwcReport)

Raises:

See also

from_swc

Load from a local SWC file.

from_asc

Load from a local Neurolucida ASC file.

Examples

>>> from braincell import Morphology
>>> morph = Morphology.from_neuromorpho(10047)
classmethod from_root(branch, *, name='soma')[source]#

Create a new morphology tree from a single root branch.

Parameters:
  • branch (Branch) – Geometry of the root branch (typically a soma).

  • name (str | None) – Name for the root branch (default "soma"). If None, auto-generated from the branch type.

Returns:

New morphology containing only the root.

Return type:

Morphology

Examples

>>> import brainunit as u
>>> from braincell import Branch, Morphology
>>> soma = Branch.from_lengths(
...     lengths=[20.0] * u.um,
...     radii=[10.0, 10.0] * u.um,
...     type="soma",
... )
>>> morph = Morphology.from_root(soma, name="soma")
>>> morph.root.name
'soma'
classmethod from_swc(path, *, options=None, mode=None, return_report=False)[source]#

Load a morphology from an SWC file.

Delegates to SwcReader for parsing and validation.

Parameters:
  • path (str or Path) – Path to the SWC file.

  • options (SwcReadOptions or None) – Reader configuration (soma handling, repairs, etc.). Uses defaults when None.

  • mode (str | None) – Convenience import mode. Supported values are "neuron" (NEURON-style soma attachment handling) and "neuromorpho" (copy soma attachment points into child branches). When provided together with options, it must match options.mode.

  • return_report (bool) – If True, return a (Morpho, SwcReport) tuple instead of just the morphology.

Returns:

The loaded morphology, optionally with a diagnostic report.

Return type:

Morphology or (Morphology, SwcReport)

See also

from_asc

Load from Neurolucida ASC format.

Examples

>>> from braincell import Morphology
>>> morph = Morphology.from_swc("neuron.swc")
>>> morph, report = Morphology.from_swc(
...     "neuron.swc", return_report=True
... )
classmethod load_checkpoint(path)[source]#

Load a morphology from a braincell checkpoint file.

Parameters:

path (str or os.PathLike) – Path to a .bcm checkpoint produced by save_checkpoint().

Returns:

The reconstructed morphology, equal to the saved tree under __eq__().

Return type:

Morphology

Raises:

CheckpointError – If the file is missing, corrupt, or is not a morph checkpoint.

See also

Morpho.save_checkpoint

Inverse operation.

Examples

>>> from braincell import Morphology
>>> morph = Morphology.load_checkpoint("neuron.bcm")
property metric: MorphoMetric#

Whole-morphology metrics exposed as a dataclass snapshot.

path_length_to_root(branch_index)[source]#

Return the path length from a branch to the root.

Note

Not yet implemented.

Parameters:

branch_index (int) – Index of the target branch in default ordering.

Returns:

Cumulative segment length along the path to root.

Return type:

Quantity

Raises:

NotImplementedError – Always, until implemented.

path_to_root(branch_index)[source]#

Return the ordered path of branch indices from root to a given branch.

Parameters:

branch_index (int) – Index of the target branch in default ordering.

Returns:

Sequence of branch indices starting at the root and ending at branch_index.

Return type:

tuple[int, ...]

property root: MorphoBranch#

The root branch of this morphology.

Returns:

Root branch node.

Return type:

MorphoBranch

save_checkpoint(path)[source]#

Write this morphology to a braincell checkpoint file.

Checkpoints are a lossless on-disk snapshot that preserves branch names, parent_x/child_x attachments (including 0.5 soma midpoints), and the auto-naming counters consulted by attach(). Unlike SWC/ASC, the round-trip is bit-exact.

Parameters:

path (str or os.PathLike) – Destination path. .bcm is appended automatically when the supplied path has no suffix.

Returns:

The final path the checkpoint was written to.

Return type:

Path

See also

Morpho.load_checkpoint

Inverse operation.

Morpho.from_swc

Load a morphology from an SWC interchange file.

Morpho.from_asc

Load a morphology from a Neurolucida ASC file.

Examples

>>> from braincell import Morphology
>>> morph = Morphology.from_swc("neuron.swc")
>>> morph.save_checkpoint("neuron.bcm")
select(expr, *, cache=None)[source]#

Evaluate a region or location-set expression on this morphology.

Parameters:
Returns:

Evaluated mask result.

Return type:

RegionMask or LocsetMask

Raises:

TypeError – If expr is not a RegionExpr or LocsetExpr.

See also

braincell.filter.RegionExpr

Region filter expressions.

braincell.filter.LocsetExpr

Location-set expressions.

Examples

>>> from braincell.filter import branch_in
>>> region = morph.select(branch_in("type", "dendrite"))
shortest_path_length(from_site, to_site)[source]#

Return the shortest path length between two sites on the tree.

A site is a (branch_index, position) pair where position is a fractional location along the branch (0 = proximal, 1 = distal).

Note

Not yet implemented.

Parameters:
  • from_site (tuple[int, float]) – Start site as (branch_index, position).

  • to_site (tuple[int, float]) – End site as (branch_index, position).

Returns:

Shortest path length through the tree between the two sites.

Return type:

Quantity

Raises:

NotImplementedError – Always, until implemented.

topo()[source]#

Return a line-oriented text view of the branch topology.

Returns:

ASCII tree representation showing parent-child relationships.

Return type:

str

Examples

>>> print(morph.topo())
soma
├── dend_0
│   ├── dend_1
│   └── dend_2
└── axon_0
vis2d(*, layout=None, shape=None, branch_type_colors=None, branch_type_edge_colors_2d=None, frustum_edge_linewidth_2d=None, backend=None, region=None, locset=None, values=None, chooser=None, ax=None, notebook=None, jupyter_backend=None, return_plotter=False, show=True, projection_plane='xy', min_branch_angle_deg=25.0, root_layout='type_split')[source]#

Visualize this morphology in 2D.

Renders the morphology tree using the Matplotlib backend. The 2-D visualization is controlled by:

  • layout — how 2-D coordinates are obtained: "projected", "stem", "balloon", or "radial_360".

  • shape — how those 2-D coordinates are drawn: "line" or "frustum".

Parameters:
  • layout (str | None) – 2-D layout choice. "projected" uses projected 3-D point geometry. "stem", "balloon", and "radial_360" use the schematic branch layout pipeline. When omitted, uses the global 2-D default configured via braincell.morph.vis.configure(...). The initial default is "fan".

  • shape (str | None) – 2-D drawing shape: "line" or "frustum". When omitted, uses the global 2-D default configured via braincell.morph.vis.configure(...). The initial default is "frustum".

  • branch_type_colors (dict[str, object] | None) – Per-branch-type colour overrides for this call only. These colours drive 2D line rendering, 2D frustum fills, and the shared branch-type theme used by 3D and topology plots.

  • branch_type_edge_colors_2d (dict[str, object] | None) – Per-branch-type frustum border colour overrides for this call only. These borders remain active even when values drives the frustum fill colours via a colormap.

  • frustum_edge_linewidth_2d (float | None) – Frustum border linewidth override for this call only.

  • backend (str | None) – Rendering backend name (e.g., "matplotlib"). Auto-selected when None.

  • region (RegionMask or None) – Evaluated region mask to attach as an overlay. Obtain one via expr.evaluate(morph) or morph.select(expr).

  • locset (LocsetMask or None) – Evaluated location-set mask to attach as an overlay.

  • values (array-like or None) – Per-branch or per-segment scalar values for colour-mapping.

  • chooser (BackendChooser or None) – Explicit backend chooser; overrides backend when given.

  • projection_plane (str) – Axis pair for layout="projected": "xy" (default), "xz", or "yz". Ignored by schematic layouts.

  • min_branch_angle_deg (float | None) – Minimum branch separation angle for schematic layouts. Ignored by layout="projected".

  • root_layout (str) – Root branching strategy for schematic layouts. Ignored by layout="projected".

  • notebook (bool | None) – Enable notebook-specific rendering when True.

  • jupyter_backend (str | None) – Jupyter backend name for notebook rendering.

  • return_plotter (bool) – If True, return the matplotlib.axes.Axes object instead of displaying the figure.

  • show (bool) – If True (default), call matplotlib.pyplot.show() after rendering. Set to False to suppress the blocking display call (useful in scripts, tests, or when embedding the axes in a larger figure).

Returns:

The matplotlib.axes.Axes when return_plotter is True; otherwise the backend’s default display result.

Return type:

object

Raises:

ValueError – If layout="projected" and any branch lacks 3-D point geometry, or if layout="projected" is combined with shape="frustum".

See also

Branch.vis2d

Quick visualization of a single branch.

vis3d

3-D visualization with the PyVista backend.

Examples

Projected layout with line drawing:

>>> morph.vis2d(layout="projected", shape="line")

Stem layout with line drawing:

>>> morph.vis2d(layout="stem", shape="line")

Stem layout with frustum drawing:

>>> morph.vis2d(layout="stem", shape="frustum")
vis3d(*, mode=None, backend=None, region=None, locset=None, values=None, chooser=None, notebook=None, jupyter_backend=None, return_plotter=False, show=True)[source]#

Visualize this morphology in 3D.

Renders the morphology as 3-D tubes using the PyVista backend. All branches must have been created with Branch.from_points() (i.e. they must carry 3-D point geometry).

Parameters:
  • mode (str | None) – Visualization mode. When omitted, uses the global 3-D default configured via braincell.morph.vis.configure(...). The initial default is "geometry".

  • backend (str | None) – Rendering backend name (e.g., "pyvista"). Auto-selected when None.

  • region (RegionMask or None) – Evaluated region mask to attach as an overlay. Obtain one via expr.evaluate(morph) or morph.select(expr).

  • locset (LocsetMask or None) – Evaluated location-set mask to attach as an overlay.

  • values (array-like or None) – Per-branch or per-segment scalar values for colour-mapping.

  • chooser (BackendChooser or None) – Explicit backend chooser; overrides backend when given.

  • notebook (bool | None) – Enable notebook-specific rendering when True. Use this in Jupyter notebooks or notebook-based docs so the 3-D scene is embedded in the cell output; leave it false/omitted when you want the raw PyVista plotter in a script.

  • jupyter_backend (str | None) – Jupyter backend name for notebook rendering. Use "html" for static HTML docs and headless builds; use interactive server/client backends such as "trame" only in a live Jupyter session configured for them.

  • return_plotter (bool) – If True, return the PyVista plotter object instead of displaying the figure.

  • show (bool) – If True (default), call the backend’s show method after rendering. Set to False to suppress the blocking display call.

Returns:

The PyVista plotter when return_plotter is True; otherwise the backend’s default display result.

Return type:

object

See also

vis2d

2-D visualization with the Matplotlib backend.

Examples

Basic 3-D rendering of a soma with one dendrite:

>>> import brainunit as u
>>> from braincell import Branch, Morphology
>>> soma = Branch.from_points(
...     points=[[0, 0, 0], [20, 0, 0]] * u.um,
...     radii=[10.0, 10.0] * u.um,
...     type="soma",
... )
>>> dend = Branch.from_points(
...     points=[[20, 0, 0], [20, 80, 0]] * u.um,
...     radii=[2.0, 1.0] * u.um,
...     type="apical_dendrite",
... )
>>> morph = Morphology.from_root(soma, name="soma")
>>> morph.soma.dend = dend
>>> morph.vis3d()

Multi-branch morphology with basal dendrites and an axon:

>>> import numpy as np
>>> basal = Branch.from_points(
...     points=[[0, 0, 0], [-30, -50, 10]] * u.um,
...     radii=[3.0, 1.5] * u.um,
...     type="basal_dendrite",
... )
>>> axon = Branch.from_points(
...     points=[[0, 0, 0], [0, 0, -100]] * u.um,
...     radii=[1.0, 0.5] * u.um,
...     type="axon",
... )
>>> morph.attach(parent="soma", child_branch=basal,
...               child_name="basal", parent_x=0.0)
>>> morph.attach(parent="soma", child_branch=axon,
...               child_name="axon", parent_x=0.5)
>>> morph.vis3d()

Highlight a region — e.g. the full extent of all dendrites:

>>> from braincell.filter import branch_in
>>> region = branch_in("type", ("apical_dendrite", "basal_dendrite")).evaluate(morph)
>>> morph.vis3d(region=region)

Mark specific locations — e.g. branch points and terminals:

>>> from braincell.filter import BranchPoints, Terminals
>>> locset = (BranchPoints() | Terminals()).evaluate(morph)
>>> morph.vis3d(locset=locset)

Retrieve the PyVista plotter for further customisation:

>>> plotter = morph.vis3d(return_plotter=True, show=False)
>>> plotter.add_text("My neuron", font_size=12)
>>> plotter.show()

Static notebook/doc rendering for headless builds:

>>> morph.vis3d(notebook=True, jupyter_backend="html")