Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

access public hoc templates with getCell or CellRef #106

Merged
merged 1 commit into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions bluecellulab/cell/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from bluecellulab.cell.section_distance import EuclideanSectionDistance
from bluecellulab.cell.sonata_proxy import SonataProxy
from bluecellulab.cell.serialized_sections import SerializedSections
from bluecellulab.cell.template import NeuronTemplate
from bluecellulab.cell.template import NeuronTemplate, public_hoc_cell
from bluecellulab.circuit.config.sections import Conditions
from bluecellulab.circuit import EmodelProperties, SynapseProperty
from bluecellulab.circuit.node_id import CellId
Expand Down Expand Up @@ -84,16 +84,15 @@ def __init__(self,
neuron_template = NeuronTemplate(template_path, morphology_path)
self.template_id = neuron_template.template_name # useful to map NEURON and python objects
self.cell = neuron_template.get_cell(template_format, gid, emodel_properties)

self.soma = self.cell.getCell().soma[0]
self.soma = public_hoc_cell(self.cell).soma[0]
# WARNING: this finitialize 'must' be here, otherwhise the
# diameters of the loaded morph are wrong
neuron.h.finitialize()

self.cellname = neuron.h.secname(sec=self.soma).split(".")[0]

# Set the gid of the cell
self.cell.getCell().gid = gid
public_hoc_cell(self.cell).gid = gid
self.gid = gid

if rng_settings is None:
Expand All @@ -113,11 +112,11 @@ def __init__(self,
# time recording needs this push
self.soma.push()
self.hocname = neuron.h.secname(sec=self.soma).split(".")[0]
self.somatic = [x for x in self.cell.getCell().somatic]
self.basal = [x for x in self.cell.getCell().basal] # dend is same as basal
self.apical = [x for x in self.cell.getCell().apical]
self.axonal = [x for x in self.cell.getCell().axonal]
self.all = [x for x in self.cell.getCell().all]
self.somatic = [x for x in public_hoc_cell(self.cell).somatic]
self.basal = [x for x in public_hoc_cell(self.cell).basal] # dend is same as basal
self.apical = [x for x in public_hoc_cell(self.cell).apical]
self.axonal = [x for x in public_hoc_cell(self.cell).axonal]
self.all = [x for x in public_hoc_cell(self.cell).all]
self.record_dt = record_dt
self.add_recordings(['self.soma(0.5)._ref_v', 'neuron.h._ref_t'],
dt=self.record_dt)
Expand Down Expand Up @@ -176,7 +175,7 @@ def init_psections(self) -> None:

# section are not serialized yet, do it now
if self.serialized is None:
self.serialized = SerializedSections(self.cell.getCell())
self.serialized = SerializedSections(public_hoc_cell(self.cell))

for isec in self.serialized.isec2sec:
hsection = self.get_hsection(isec)
Expand Down Expand Up @@ -257,7 +256,7 @@ def get_hsection(self, section_id: int | float) -> NeuronSection:
section_id = int(section_id)
# section are not serialized yet, do it now
if self.serialized is None:
self.serialized = SerializedSections(self.cell.getCell())
self.serialized = SerializedSections(public_hoc_cell(self.cell))

try:
sec_ref = self.serialized.isec2sec[section_id]
Expand Down Expand Up @@ -286,8 +285,8 @@ def enable_ttx(self) -> None:
Enable TTX by inserting TTXDynamicsSwitch and setting ttxo to
1.0
"""
if hasattr(self.cell.getCell(), 'enable_ttx'):
self.cell.getCell().enable_ttx()
if hasattr(public_hoc_cell(self.cell), 'enable_ttx'):
public_hoc_cell(self.cell).enable_ttx()
else:
self._default_enable_ttx()

Expand All @@ -297,8 +296,8 @@ def disable_ttx(self) -> None:
Disable TTX by inserting TTXDynamicsSwitch and setting ttxo to
1e-14
"""
if hasattr(self.cell.getCell(), 'disable_ttx'):
self.cell.getCell().disable_ttx()
if hasattr(public_hoc_cell(self.cell), 'disable_ttx'):
public_hoc_cell(self.cell).disable_ttx()
else:
self._default_disable_ttx()

Expand Down Expand Up @@ -409,14 +408,14 @@ def get_voltage_recording(

def add_allsections_voltagerecordings(self):
"""Add a voltage recording to every section of the cell."""
all_sections = self.cell.getCell().all
all_sections = public_hoc_cell(self.cell).all
for section in all_sections:
self.add_voltage_recording(section, segx=0.5, dt=self.record_dt)

def get_allsections_voltagerecordings(self) -> dict[str, np.ndarray]:
"""Get all the voltage recordings from all the sections."""
all_section_voltages = {}
all_sections = self.cell.getCell().all
all_sections = public_hoc_cell(self.cell).all
for section in all_sections:
recording = self.get_voltage_recording(section, segx=0.5)
all_section_voltages[section.name()] = recording
Expand Down Expand Up @@ -494,11 +493,11 @@ def create_netcon_spikedetector(self, target: HocObjectType, location: str, thre
ValueError: If the spike detection location is not 'soma' or 'AIS'.
"""
if location == "soma":
sec = self.cell.getCell().soma[0]
source = self.cell.getCell().soma[0](1)._ref_v
sec = public_hoc_cell(self.cell).soma[0]
source = public_hoc_cell(self.cell).soma[0](1)._ref_v
elif location == "AIS":
sec = self.cell.getCell().axon[1]
source = self.cell.getCell().axon[1](0.5)._ref_v
sec = public_hoc_cell(self.cell).axon[1]
source = public_hoc_cell(self.cell).axon[1](0.5)._ref_v
else:
raise ValueError("Spike detection location must be soma or AIS")
netcon = bluecellulab.neuron.h.NetCon(source, target, sec=sec)
Expand Down Expand Up @@ -678,14 +677,14 @@ def somatic_branches(self):
if "dend" in secname:
dendnumber = int(
secname.split("dend")[1].split("[")[1].split("]")[0])
secnumber = int(self.cell.getCell().nSecAxonalOrig +
self.cell.getCell().nSecSoma + dendnumber)
secnumber = int(public_hoc_cell(self.cell).nSecAxonalOrig +
public_hoc_cell(self.cell).nSecSoma + dendnumber)
elif "apic" in secname:
apicnumber = int(secname.split(
"apic")[1].split("[")[1].split("]")[0])
secnumber = int(self.cell.getCell().nSecAxonalOrig +
self.cell.getCell().nSecSoma +
self.cell.getCell().nSecBasal + apicnumber)
secnumber = int(public_hoc_cell(self.cell).nSecAxonalOrig +
public_hoc_cell(self.cell).nSecSoma +
public_hoc_cell(self.cell).nSecBasal + apicnumber)
logger.info((apicnumber, secnumber))
else:
raise Exception(
Expand Down Expand Up @@ -814,8 +813,8 @@ def delete(self):
"""Delete the cell."""
self.delete_plottable()
if hasattr(self, 'cell') and self.cell is not None:
if self.cell.getCell() is not None and hasattr(self.cell.getCell(), 'clear'):
self.cell.getCell().clear()
if public_hoc_cell(self.cell) is not None and hasattr(public_hoc_cell(self.cell), 'clear'):
public_hoc_cell(self.cell).clear()

self.connections = None
self.synapses = None
Expand Down
11 changes: 11 additions & 0 deletions bluecellulab/cell/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@
logger = logging.getLogger(__name__)


def public_hoc_cell(cell: HocObjectType) -> HocObjectType:
"""Retrieve the hoc cell to access public hoc functions/attributes."""
if hasattr(cell, "getCell"):
return cell.getCell()
elif hasattr(cell, "CellRef"):
return cell.CellRef
else:
raise BluecellulabError("""Public cell properties cannot be accessed
from the hoc model. Either getCell() or CellRef needs to be provided""")


class NeuronTemplate:
"""NeuronTemplate representation."""

Expand Down
55 changes: 48 additions & 7 deletions tests/test_cell/test_template.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
"""Unit tests for template.py module."""

from pathlib import Path
from unittest.mock import Mock

import pytest

from bluecellulab.cell.template import NeuronTemplate
from bluecellulab.cell.template import NeuronTemplate, public_hoc_cell
from bluecellulab.circuit.circuit_access import EmodelProperties
from bluecellulab.exceptions import BluecellulabError


parent_dir = Path(__file__).resolve().parent.parent

hoc_path = (
hipp_hoc_path = (
parent_dir
/ "examples"
/ "hippocampus_opt_cell_template"
/ "electrophysiology"
/ "cell.hoc"
)
morph_path = (
hipp_morph_path = (
parent_dir / "examples" / "hippocampus_opt_cell_template" / "morphology" / "cell.asc"
)

v6_hoc_path = (
parent_dir / "examples" / "circuit_sonata_quick_scx" / "components" / "hoc" / "cADpyr_L2TPC.hoc"
)

v6_morph_path = (
parent_dir / "examples" / "circuit_sonata_quick_scx" / "components" / "morphologies" / "asc" / "rr110330_C3_idA.asc"
)


def test_get_cell_with_bluepyopt_template():
"""Unit test for the get_cell method with bluepyopt_template."""
template = NeuronTemplate(hoc_path, morph_path)
template = NeuronTemplate(hipp_hoc_path, hipp_morph_path)
cell = template.get_cell("bluepyopt", None, None)
assert cell.hname() == f"bACnoljp_bluecellulab_{(hex(id(template)))}[0]"

Expand All @@ -33,8 +44,38 @@ def test_neuron_template_init():
missing_file = "missing_file"

with pytest.raises(FileNotFoundError):
NeuronTemplate(missing_file, morph_path)
NeuronTemplate(missing_file, hipp_morph_path)
with pytest.raises(FileNotFoundError):
NeuronTemplate(hoc_path, missing_file)
NeuronTemplate(hipp_hoc_path, missing_file)

NeuronTemplate(hipp_hoc_path, hipp_morph_path)


def test_public_hoc_cell_bluepyopt_template():
"""Unit test for public_hoc_cell."""
template = NeuronTemplate(hipp_hoc_path, hipp_morph_path)
cell = template.get_cell("bluepyopt", None, None)
hoc_public = public_hoc_cell(cell)
assert hoc_public.gid == 0.0


def test_public_hoc_cell_v6_template():
"""Unit test for public_hoc_cell."""
template = NeuronTemplate(v6_hoc_path, v6_morph_path)
emodel_properties = EmodelProperties(
threshold_current=1.1433533430099487,
holding_current=1.4146618843078613,
ais_scaler=1.4561502933502197,
soma_scaler=1.0
)
cell = template.get_cell("v6_adapted", 5, emodel_properties)
hoc_public = public_hoc_cell(cell)
assert hoc_public.gid == 5.0


NeuronTemplate(hoc_path, morph_path)
def test_public_hoc_cell_failure():
"""Unit test for public_hoc_cell when neither getCell nor CellRef is provided."""
cell_without_getCell_or_CellRef = Mock(spec=[]) # spec=[] ensures no attributes exist
with pytest.raises(BluecellulabError) as excinfo:
public_hoc_cell(cell_without_getCell_or_CellRef)
assert "Public cell properties cannot be accessed" in str(excinfo.value)