From 2a829335e0a39925020ac7b9efcccac6682b17a2 Mon Sep 17 00:00:00 2001 From: Katharina Duecker Date: Fri, 10 Jan 2025 12:46:01 -0500 Subject: [PATCH] [MRG] Add name parameter to tonic bias and add tonic biases to different sections (#922) * tonic bias options * update defaults * describe new parameters * stop tracking arm64 * flake8 * unit tests * change sect_name to section for consistency * change sect_name to section for consistency test_gui * workaround backwards compatibility * change CSD back * add deprecation warning * fix typos * unit tests * clean up section warning * refactor: changed the default value from None to 'soma' * chore: updated test asset with section attributes in external_biases * chore: removed 'section' from ignore_keys in test_gui.check_equal_networks * fix test * add bias_name test * update value error * fix regex test * Update hnn_core/tests/test_network.py Co-authored-by: Mainak Jas * Update hnn_core/tests/test_network.py Co-authored-by: Mainak Jas * Update hnn_core/tests/test_network.py Co-authored-by: Mainak Jas * Update hnn_core/network.py Co-authored-by: Mainak Jas * Update hnn_core/network.py Co-authored-by: Mainak Jas * update whats_new and fix flake8 * update whats_new --------- Co-authored-by: George Dang <53052793+gtdang@users.noreply.github.com> Co-authored-by: Mainak Jas --- doc/whats_new.rst | 4 +++ hnn_core/cell.py | 12 ++++--- hnn_core/network.py | 36 ++++++++++++++----- hnn_core/network_builder.py | 8 ++--- .../tests/assets/jones2009_3x3_drives.json | 12 ++++--- hnn_core/tests/test_network.py | 15 +++++++- 6 files changed, 66 insertions(+), 21 deletions(-) diff --git a/doc/whats_new.rst b/doc/whats_new.rst index 07b130ecb..ee781b59d 100644 --- a/doc/whats_new.rst +++ b/doc/whats_new.rst @@ -41,6 +41,10 @@ API - Add :func:`~hnn_core.CellResponse.spike_times_by_type` to get cell spiking times organized by cell type, by `Mainak Jas`_ in :gh:`916`. +- Add option to apply a tonic bias to any compartment of the cell, and option to + add multiple biases per simulation and cell :func:`hnn_core.network.add_tonic_bias`, + by `Katharina Duecker`_ in :gh:`922`. + .. _0.4: 0.4 diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 7e533f087..3ee185cd9 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -728,8 +728,9 @@ def _insert_dipole(self, sec_name_apical): dpp.ztan = seg_lens_z[-1] self.dipole = h.Vector().record(self.dpl_ref) - def create_tonic_bias(self, amplitude, t0, tstop, loc=0.5): - """Create tonic bias at the soma. + def create_tonic_bias(self, amplitude, t0, tstop, section='soma', + loc=0.5): + """Create tonic bias at defined section. Parameters ---------- @@ -739,10 +740,13 @@ def create_tonic_bias(self, amplitude, t0, tstop, loc=0.5): The start time of tonic input (in ms). tstop : float The end time of tonic input (in ms). + section : str + Section tonic input is applied to. loc : float (0 to 1) - The location of the input in the soma section. + The location of the input in the section. """ - stim = h.IClamp(self._nrn_sections['soma'](loc)) + + stim = h.IClamp(self._nrn_sections[section](loc)) stim.delay = t0 stim.dur = tstop - t0 stim.amp = amplitude diff --git a/hnn_core/network.py b/hnn_core/network.py index c00de12b0..074ca5c5d 100644 --- a/hnn_core/network.py +++ b/hnn_core/network.py @@ -1133,7 +1133,8 @@ def _instantiate_drives(self, tstop, n_trials=1): self.external_drives[ drive['name']]['events'].append(event_times) - def add_tonic_bias(self, *, cell_type=None, amplitude, t0=0, tstop=None): + def add_tonic_bias(self, *, cell_type=None, section='soma', + bias_name='tonic', amplitude, t0=0, tstop=None): """Attaches parameters of tonic bias input for given cell types Parameters @@ -1142,6 +1143,11 @@ def add_tonic_bias(self, *, cell_type=None, amplitude, t0=0, tstop=None): The name of the cell type to add a tonic input. When supplied, a float value must be provided with the `amplitude` keyword. Valid inputs are those listed in `net.cell_types`. + section : str + name of cell section the bias should be applied to. + See net.cell_types[cell_type].sections.keys() + bias_name : str + The name of the bias. amplitude: dict | float A dictionary of cell type keys (str) to amplitude values (float). Valid inputs for cell types are those listed in `net.cell_types`. @@ -1169,6 +1175,7 @@ def add_tonic_bias(self, *, cell_type=None, amplitude, t0=0, tstop=None): _validate_type(amplitude, (float, int), 'amplitude') _add_cell_type_bias(network=self, cell_type=cell_type, + section=section, bias_name=bias_name, amplitude=float(amplitude), t_0=t0, t_stop=tstop) else: @@ -1180,6 +1187,7 @@ def add_tonic_bias(self, *, cell_type=None, amplitude, t0=0, tstop=None): for _cell_type, _amplitude in amplitude.items(): _add_cell_type_bias(network=self, cell_type=_cell_type, + section=section, bias_name=bias_name, amplitude=_amplitude, t_0=t0, t_stop=tstop) @@ -1648,7 +1656,7 @@ def __repr__(self): def _add_cell_type_bias(network: Network, amplitude: Union[float, dict], - cell_type=None, + cell_type=None, section='soma', bias_name='tonic', t_0=0, t_stop=None): if network is None: @@ -1665,15 +1673,27 @@ def _add_cell_type_bias(network: Network, amplitude: Union[float, dict], f'{list(network.cell_types.keys())}. ' f'Got {cell_type}') - if 'tonic' not in network.external_biases: - network.external_biases['tonic'] = dict() + if bias_name not in network.external_biases: + network.external_biases[bias_name] = dict() - if cell_type in network.external_biases['tonic']: - raise ValueError(f'Tonic bias already defined for {cell_type}') + if cell_type in network.external_biases[bias_name]: + raise ValueError(f'Bias named {bias_name} already defined ' + f'for {cell_type}') cell_type_bias = { 'amplitude': amplitude, 't0': t_0, - 'tstop': t_stop + 'tstop': t_stop, + 'section': section } - network.external_biases['tonic'][cell_type] = cell_type_bias + + sections = list(network.cell_types[cell_type].sections.keys()) + + # error when section is defined that doesn't exist. + if section not in sections: + raise ValueError(f"section must be one of {sections}. " + f"Got {section}.") + else: + cell_type_bias['section'] = section + + network.external_biases[bias_name][cell_type] = cell_type_bias diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 230f20e06..6995e0714 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -441,10 +441,10 @@ def _create_cells_and_drives(self, threshold, record_vsec=False, else: cell.build() # add tonic biases - if ('tonic' in self.net.external_biases and - src_type in self.net.external_biases['tonic']): - cell.create_tonic_bias(**self.net.external_biases - ['tonic'][src_type]) + for bias in self.net.external_biases: + if src_type in self.net.external_biases[bias]: + cell.create_tonic_bias(**self.net.external_biases + [bias][src_type]) cell.record(record_vsec, record_isec, record_ca) # this call could belong in init of a _Cell (with threshold)? diff --git a/hnn_core/tests/assets/jones2009_3x3_drives.json b/hnn_core/tests/assets/jones2009_3x3_drives.json index 149a86fde..0b80b0561 100644 --- a/hnn_core/tests/assets/jones2009_3x3_drives.json +++ b/hnn_core/tests/assets/jones2009_3x3_drives.json @@ -2128,22 +2128,26 @@ "L2_pyramidal": { "amplitude": 1.0, "t0": 0, - "tstop": null + "tstop": null, + "section": "soma" }, "L5_pyramidal": { "amplitude": 0.0, "t0": 0, - "tstop": null + "tstop": null, + "section": "soma" }, "L2_basket": { "amplitude": 0.0, "t0": 0, - "tstop": null + "tstop": null, + "section": "soma" }, "L5_basket": { "amplitude": 0.0, "t0": 0, - "tstop": null + "tstop": null, + "section": "soma" } } }, diff --git a/hnn_core/tests/test_network.py b/hnn_core/tests/test_network.py index 5b7d7a742..bb0eba484 100644 --- a/hnn_core/tests/test_network.py +++ b/hnn_core/tests/test_network.py @@ -855,9 +855,22 @@ def test_tonic_biases(): assert 'tonic' in net.external_biases assert 'L5_pyramidal' not in net.external_biases['tonic'] assert net.external_biases['tonic']['L2_pyramidal']['t0'] == 0 - with pytest.raises(ValueError, match=r'Tonic bias already defined for.*$'): + with pytest.raises(ValueError, match=r'Bias named tonic already defined ' + r'for.*$'): net.add_tonic_bias(amplitude=tonic_bias_2) + net = jones_2009_model() + net.add_tonic_bias(amplitude=tonic_bias_2, bias_name='tonic_2', t0=100) + assert 'tonic_2' in net.external_biases + assert net.external_biases['tonic_2']['L2_pyramidal']['t0'] == 100 + + # non-existent section + net.external_biases = dict() + + with pytest.raises(ValueError, match=(r'section must be one of .*' + ' Got apical_4.')): + net.add_tonic_bias(amplitude={'L2_pyramidal': .5}, section='apical_4') + def test_network_mesh(): """Test mesh for defining cell positions biases."""