Morphology#
- class braincell.Morphology(*, root_name, root_branch)[source]#
Mutable morphology tree for authoring, querying, and visualization.
Morphois the central entry point for building neuron morphologies. It owns a tree ofMorphoBranchnodes, each wrapping an immutableBranchgeometry. Children are attached viaattach(), attribute assignment on aMorphoBranch, orthe
branch[parent_x].child_name = branchsyntax sugar.Prefer the factory classmethods
from_root(),from_swc(), andfrom_asc()over the raw constructor.- Parameters:
- Raises:
ValueError – If root_name is a reserved name or already taken, or is not a valid Python identifier.
See also
BranchImmutable segment geometry.
MorphoBranchTree-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 theMorphoinstance.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. IfNone, 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), or1(distal, default).child_x (
float) – Attachment point on the child branch:0(proximal, default) or1(distal).
- Returns:
The newly attached child branch node.
- Return type:
- Raises:
ValueError – If parent_x or child_x is invalid, if parent_x is
0.5on 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=0attaches 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:
- Returns:
The requested branch.
- Return type:
- 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:
- 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:
See also
branch_by_orderQuery 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
AscReaderfor parsing and validation.- Parameters:
- Returns:
The loaded morphology, optionally with a diagnostic report.
- Return type:
Morphology or (Morphology, AscReport)
See also
from_swcLoad 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 withfrom_swc()usingmode="neuromorpho". Already-cached neurons are not re-downloaded unlessoverwrite=True.- Parameters:
neuron_id (int) – NeuroMorpho.Org numeric identifier (e.g.
10047).cache_dir (str or Path or None) – Cache root.
Noneresolves 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) – IfTrue, also return theSwcReportfrom parsing.overwrite (
bool) – IfTrue, 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:
NeuroMorphoNotFoundError – If
neuron_iddoes not exist upstream.NeuroMorphoHTTPError – On non-recoverable HTTP errors.
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:
- Returns:
New morphology containing only the root.
- Return type:
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
SwcReaderfor 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 matchoptions.mode.return_report (
bool) – IfTrue, 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_ascLoad 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
.bcmcheckpoint produced bysave_checkpoint().- Returns:
The reconstructed morphology, equal to the saved tree under
__eq__().- Return type:
- Raises:
CheckpointError – If the file is missing, corrupt, or is not a morph checkpoint.
See also
Morpho.save_checkpointInverse 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.
- property root: MorphoBranch#
The root branch of this morphology.
- Returns:
Root branch node.
- Return type:
- 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_xattachments (including0.5soma midpoints), and the auto-naming counters consulted byattach(). Unlike SWC/ASC, the round-trip is bit-exact.- Parameters:
path (str or os.PathLike) – Destination path.
.bcmis 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_checkpointInverse operation.
Morpho.from_swcLoad a morphology from an SWC interchange file.
Morpho.from_ascLoad 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:
expr (RegionExpr or LocsetExpr) – Filter expression to evaluate.
cache (SelectionCache or None) – Optional cache for repeated evaluations.
- Returns:
Evaluated mask result.
- Return type:
- Raises:
TypeError – If expr is not a
RegionExprorLocsetExpr.
See also
braincell.filter.RegionExprRegion filter expressions.
braincell.filter.LocsetExprLocation-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:
- 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:
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 viabraincell.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 viabraincell.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 whenvaluesdrives 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)ormorph.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 forlayout="projected":"xy"(default),"xz", or"yz". Ignored by schematic layouts.min_branch_angle_deg (
float|None) – Minimum branch separation angle for schematic layouts. Ignored bylayout="projected".root_layout (
str) – Root branching strategy for schematic layouts. Ignored bylayout="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 thematplotlib.axes.Axesobject instead of displaying the figure.show (
bool) – If True (default), callmatplotlib.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.Axeswhen return_plotter is True; otherwise the backend’s default display result.- Return type:
- Raises:
ValueError – If
layout="projected"and any branch lacks 3-D point geometry, or iflayout="projected"is combined withshape="frustum".
See also
Branch.vis2dQuick visualization of a single branch.
vis3d3-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 viabraincell.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)ormorph.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:
See also
vis2d2-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")