Skip to content

Add SONATA point-process cell support#51

Draft
ilkilic wants to merge 18 commits intomainfrom
point-process
Draft

Add SONATA point-process cell support#51
ilkilic wants to merge 18 commits intomainfrom
point-process

Conversation

@ilkilic
Copy link
Copy Markdown
Collaborator

@ilkilic ilkilic commented Jan 9, 2026

This PR adds support for SONATA point-process cells.

Point-process cells are instantiated from SONATA model_type = "point_process" and model_template, mapping directly to NEURON point mechanisms defined in HOC/MOD files.

What is included

  • New HocPointProcessCell for SONATA point-process populations
  • Mechanism instantiation driven by model_template (e.g. nrn:IntFire1, hoc:)
  • Integration with CircuitSimulation (instantiate_gids, callbacks, deletion)
  • Spike detection and recording compatible with existing simulation flow
  • Support for SONATA inputs and SynapseReplay via _add_stimuli
  • Compatibility with SONATA edge loading through existing SonataCircuitAccess

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 9, 2026

Codecov Report

❌ Patch coverage is 93.79310% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
bluecellulab/cell/point_process.py 92.17% 9 Missing ⚠️
bluecellulab/circuit_simulation.py 83.33% 4 Missing ⚠️
bluecellulab/reports/utils.py 75.00% 2 Missing ⚠️
tests/test_allen_v1/test_ringcells_allen_v1.py 96.92% 2 Missing ⚠️
bluecellulab/point/point_connection.py 94.44% 1 Missing ⚠️
Files with missing lines Coverage Δ
...ab/circuit/circuit_access/sonata_circuit_access.py 95.90% <100.00%> (+0.09%) ⬆️
bluecellulab/circuit/synapse_properties.py 69.23% <100.00%> (+1.23%) ⬆️
bluecellulab/point/connection_params.py 100.00% <100.00%> (ø)
bluecellulab/synapse/__init__.py 100.00% <100.00%> (ø)
bluecellulab/synapse/synapse_factory.py 90.32% <100.00%> (-0.49%) ⬇️
bluecellulab/synapse/synapse_types.py 85.43% <100.00%> (+2.40%) ⬆️
bluecellulab/point/point_connection.py 94.44% <94.44%> (ø)
bluecellulab/reports/utils.py 93.81% <75.00%> (-0.84%) ⬇️
tests/test_allen_v1/test_ringcells_allen_v1.py 96.92% <96.92%> (ø)
bluecellulab/circuit_simulation.py 86.67% <83.33%> (+1.92%) ⬆️
... and 1 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jamesgking jamesgking self-assigned this Mar 9, 2026
The logic for loading from sonata files is to swap out the list of parameters if the BBP TYPE is not present

Likewise, when instantiating a synapse object, if no TYPE field is present, assume it is for allen and
create a Exp2Syn synapse
…e instantiated.

The reporting adds try/except since these don't have a soma
… target point neurons

and deliver replay spikes to the proper netcon
Removed call to finalize for point process type cells since this is not needed
Updated allen tests to collection cell_info and get ExpSyn data as consequence
…re netcon code

Remove more unused code sections; can be added when use case example is available
Added additional test for invalid Mechanism given to HocPointProcessCell
Copy link
Copy Markdown
Collaborator

@darshanmandge darshanmandge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @jamesgking, for adding this to bluecellulab! I have left some comments.

logger = logging.getLogger(__name__)


class BasePointProcessCell(Cell):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This inherits from Cell but never calls super().__init__(), so none of Cell's attributes (self.recordings, self.soma, self.cell, self.record_dt, etc.) are initialized. This means inherited methods like get_time(), get_recording(), etc., will crash with AttributeError. The bare except Exception: continue blocks added in utils.py may catch the errors though. Maybe initialising the minimum required attributes so inherited methods degrade gracefully instead of relying on catch-all exception handlers.

vs.play(vec)

if self.pointcell is None:
raise ValueError("attempting to add replay spikes with valid pointprocess")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the message be ... without valid pointprocess"?

Comment on lines +131 to +136
if not hasattr(self, "_replay_vecs"):
self._replay_vecs: list[h.Vector] = []
if not hasattr(self, "_replay_vecstims"):
self._replay_vecstims: list[h.VecStim] = []
if not hasattr(self, "_replay_netcons"):
self._replay_netcons: list[h.NetCon] = []
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could _replay_vecs, _replay_vecstims and _replay_netcon be initialised in init() instead?

Comment on lines +177 to +178
point_params = PointProcessConnParameters(syn_description[SynapseProperty.PRE_GID], syn_description[SynapseProperty.PRE_GID],
syn_description[SynapseProperty.AXONAL_DELAY])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PointProcessConnParameters constructor is called as PointProcessConnParameters(PRE_GID, PRE_GID, AXONAL_DELAY). The dataclass fields are (sgid, delay, weight). So delay gets PRE_GID, and weight gets AXONAL_DELAY. Looks like a bug. Likely should be something like PointProcessConnParameters(sgid=PRE_GID, delay=AXONAL_DELAY, weight=conductance).

point_params = PointProcessConnParameters(syn_description[SynapseProperty.PRE_GID], syn_description[SynapseProperty.PRE_GID],
syn_description[SynapseProperty.AXONAL_DELAY])

self.pointConn = PointProcessConnection([point_params])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is assigned but never stored in self.connections or used anywhere else. The connection object is created and then effectively discarded on the next call. Is this intentional?


for cell_id, cell in cells.items():
sec = cell.soma
try:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch-all try block silently skips cells that lack a soma. This can hide real errors in morphological cells. Maybe isinstance(cell, BasePointProcessCell) to skip point cells explicitly would be better and let other errors propagate?

cell = cells.get(cell_id)
if cell is None:
continue
try:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, except the catch-all try block silently skips cells that lack a get_time() method.

PRE_GID = "pre_gid"
AXONAL_DELAY = "axonal_delay"
POST_SECTION_ID = "post_section_id"
POST_SECTION_POS = "post_section_pos"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POST_SECTION_POS was added here but never referenced anywhere in code. Should it be removed?

SIM_DIR = Path(__file__).parent.parent.absolute() / "examples" / "ringtest_allen_v1"


def test_cell_point_create(capsys):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In all 4 test functions, the capsys parameter is declared but never used. Can it be removed?

assert (threshold_vals[cell_id.id] == cell.threshold)


def test_point_process_cell_rejects_none(capsys):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both in lines 98-105 and 107-115: These tests silently pass if the exception is NOT raised. You can instead use pytest.raises(ValueError, match=...) and pytest.raises(BluecellulabError, match=...).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants