From a430ee91bddf646f264a28f28f0ac54d4c13bfd6 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 28 Apr 2021 22:58:00 -0400 Subject: [PATCH 01/49] MAINT: Unify set_geometry --- hnn_core/basket.py | 15 --------------- hnn_core/cell.py | 29 +++++++++++++++++++++++++++++ hnn_core/params_default.py | 1 + hnn_core/pyramidal.py | 22 +--------------------- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/hnn_core/basket.py b/hnn_core/basket.py index 2aa773cce..5bd0d7181 100644 --- a/hnn_core/basket.py +++ b/hnn_core/basket.py @@ -29,10 +29,6 @@ def __init__(self, pos, cell_name='Basket', gid=None): _Cell.__init__(self, self.props, gid=gid) # store cell name for later self.name = cell_name - - # Define 3D shape and position of cell. By default neuron uses xy plane - # for height and xz plane for depth. This is opposite for model as a - # whole, but convention is followed in this function ease use of gui. self.set_geometry() self.synapses = dict() @@ -52,17 +48,6 @@ def _get_soma_props(self, cell_name, pos): def secs(self): return _secs_Basket() - def set_geometry(self): - """Define geometry.""" - sec_pts, sec_lens, sec_diams, _, _ = self.secs() - for sec in [self.soma]: - h.pt3dclear(sec=sec) - sec_name = sec.name().split('_', 1)[1] - for pt in sec_pts[sec_name]: - h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) - sec.L = sec_lens[sec_name] - sec.diam = sec_diams[sec_name] - def get_sections(self): """Get sections.""" return [self.soma] diff --git a/hnn_core/cell.py b/hnn_core/cell.py index c7f99c018..b40c9c44e 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -108,6 +108,8 @@ def __init__(self, soma_props, gid=None): # variable for the list_IClamp self.list_IClamp = None self.soma_props = soma_props + self.list_dend = list() + self.dends = list() self.create_soma() self.rec_v = h.Vector() self.rec_i = dict() @@ -360,3 +362,30 @@ def _pardistance(self, pos_pre): dx = self.pos[0] - pos_pre[0] dy = self.pos[1] - pos_pre[1] return np.sqrt(dx**2 + dy**2) + + def set_geometry(self): + """Define geometry.""" + # Define 3D shape and position of cell. By default neuron uses xy plane + # for height and xz plane for depth. This is opposite for model as a + # whole, but convention is followed in this function ease use of gui. + sec_pts, sec_lens, sec_diams, _, topology = self.secs() + for sec in [self.soma] + self.list_dend: + h.pt3dclear(sec=sec) + sec_name = sec.name().split('_', 1)[1] + for pt in sec_pts[sec_name]: + h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) + sec.L = sec_lens[sec_name] + sec.diam = sec_diams[sec_name] + + if topology is None: + topology = list() + + # Connects sections of THIS cell together. + for connection in topology: + # XXX: risky to use self.soma as default. Unfortunately there isn't + # a dictionary with all the sections (including soma) + parent_sec = self.dends.get(connection[0], self.soma) + parent_loc = connection[1] + child_sec = self.dends.get(connection[2], self.soma) + child_loc = connection[3] + child_sec.connect(parent_sec, parent_loc, child_loc) diff --git a/hnn_core/params_default.py b/hnn_core/params_default.py index 4779a63dc..3d5fbc02c 100644 --- a/hnn_core/params_default.py +++ b/hnn_core/params_default.py @@ -440,6 +440,7 @@ def _secs_L2Pyr(): def _secs_L5Pyr(): """The geometry of the default sections in L5Pyr Neuron.""" + # Neuron shape based on Jones et al., 2009 sec_pts = { 'soma': [[0, 0, 0], [0, 23, 0]], 'apical_trunk': [[0, 23, 0], [0, 83, 0]], diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index 6be1fe3d7..bb27fa212 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -119,27 +119,7 @@ def set_geometry(self, p_dend): * cm: membrane capacitance in micro-Farads * Ra: axial resistivity in ohm-cm """ - sec_pts, sec_lens, sec_diams, _, topology = self.secs() - - # Connects sections of THIS cell together. - for connection in topology: - # XXX: risky to use self.soma as default. Unfortunately there isn't - # a dictionary with all the sections (including soma) - parent_sec = self.dends.get(connection[0], self.soma) - parent_loc = connection[1] - child_sec = self.dends.get(connection[2], self.soma) - child_loc = connection[3] - child_sec.connect(parent_sec, parent_loc, child_loc) - - # Neuron shape based on Jones et al., 2009 - for sec in [self.soma] + self.list_dend: - h.pt3dclear(sec=sec) - sec_name = sec.name().split('_', 1)[1] - for pt in sec_pts[sec_name]: - h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) - sec.L = sec_lens[sec_name] - sec.diam = sec_diams[sec_name] - + _Cell.set_geometry(self) # resets length,diam,etc. based on param specification for key in p_dend: # set dend props From ff26c15783b5b3044f317adf3837e0dfa5db6715 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 00:12:44 -0400 Subject: [PATCH 02/49] ENH: more simplification --- hnn_core/basket.py | 2 - hnn_core/pyramidal.py | 86 ++++++++++++++----------------------------- 2 files changed, 28 insertions(+), 60 deletions(-) diff --git a/hnn_core/basket.py b/hnn_core/basket.py index 5bd0d7181..c8ad7efd1 100644 --- a/hnn_core/basket.py +++ b/hnn_core/basket.py @@ -3,8 +3,6 @@ # Authors: Mainak Jas # Sam Neymotin -from neuron import h - from .cell import _Cell from .params_default import _secs_Basket diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index bb27fa212..42cbb2a25 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -18,6 +18,22 @@ # Units for gbar: S/cm^2 unless otherwise noted +def _flat_to_nested(params, prefix, level1_keys, level2_keys): + """Convert a flat dictionary to a nested dictionary.""" + nested_dict = dict() + for level1_key in level1_keys: + level2_dict = dict() + for key in level2_keys: + if key in ['Ra', 'cm']: + middle = 'dend' + else: + # map apicaltrunk -> apical_trunk etc. + middle = level1_key.replace('_', '') + level2_dict[key] = params[f'{prefix}_{middle}_{key}'] + nested_dict[level1_key] = level2_dict + return nested_dict + + class Pyr(_Cell): """Pyramidal neuron. @@ -81,7 +97,10 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.list_dend = [] self.celltype = celltype - p_dend = self._get_dend_props(p_all) + level2_keys = ['L', 'diam', 'Ra', 'cm'] + p_dend = _flat_to_nested(p_all, prefix=self.name, + level1_keys=self.section_names(), + level2_keys=level2_keys) p_syn = self._get_syn_props(p_all) # Geometry @@ -158,63 +177,6 @@ def get_sections(self): ls.append(self.dends[key]) return ls - def _get_dend_props(self, p_all): - """Returns hardcoded dendritic properties.""" - props = { - 'apical_trunk': { - 'L': p_all['%s_apicaltrunk_L' % self.name], - 'diam': p_all['%s_apicaltrunk_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - 'apical_1': { - 'L': p_all['%s_apical1_L' % self.name], - 'diam': p_all['%s_apical1_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - 'apical_tuft': { - 'L': p_all['%s_apicaltuft_L' % self.name], - 'diam': p_all['%s_apicaltuft_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - 'apical_oblique': { - 'L': p_all['%s_apicaloblique_L' % self.name], - 'diam': p_all['%s_apicaloblique_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - 'basal_1': { - 'L': p_all['%s_basal1_L' % self.name], - 'diam': p_all['%s_basal1_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - 'basal_2': { - 'L': p_all['%s_basal2_L' % self.name], - 'diam': p_all['%s_basal2_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - 'basal_3': { - 'L': p_all['%s_basal3_L' % self.name], - 'diam': p_all['%s_basal3_diam' % self.name], - 'cm': p_all['%s_dend_cm' % self.name], - 'Ra': p_all['%s_dend_Ra' % self.name], - }, - } - if self.name == 'L5Pyr': - props.update({ - 'apical_2': { - 'L': p_all['L5Pyr_apical2_L'], - 'diam': p_all['L5Pyr_apical2_diam'], - 'cm': p_all['L5Pyr_dend_cm'], - 'Ra': p_all['L5Pyr_dend_Ra'], - }, - }) - return props - def _get_syn_props(self, p_all): return { 'ampa': { @@ -303,6 +265,10 @@ class L2Pyr(Pyr): def __init__(self, pos=None, override_params=None, gid=None): Pyr.__init__(self, pos, 'L2_pyramidal', override_params, gid=gid) + def section_names(self): + return ['apical_trunk', 'apical_1', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + def _get_soma_props(self, pos, p_all): """Hardcoded somatic properties.""" return { @@ -389,6 +355,10 @@ def __init__(self, pos=None, override_params=None, gid=None): corresponding params in p.""" Pyr.__init__(self, pos, 'L5_pyramidal', override_params, gid=gid) + def section_names(self): + return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + def secs(self): return _secs_L5Pyr() From 79807f6de0d64faa37b0719e6b75da3dee03525e Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 00:43:34 -0400 Subject: [PATCH 03/49] ENH: Unify more --- hnn_core/basket.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hnn_core/basket.py b/hnn_core/basket.py index c8ad7efd1..5ea501edd 100644 --- a/hnn_core/basket.py +++ b/hnn_core/basket.py @@ -29,6 +29,8 @@ def __init__(self, pos, cell_name='Basket', gid=None): self.name = cell_name self.set_geometry() self.synapses = dict() + self._synapse_create() + self.set_biophysics() def set_biophysics(self): self.soma.insert('hh2') @@ -79,9 +81,6 @@ def __init__(self, pos, gid=None): # Note: Basket cell properties set in BasketSingle()) BasketSingle.__init__(self, pos, cell_name='L2Basket', gid=gid) self.celltype = 'L2_basket' - - self._synapse_create() - self.set_biophysics() self.sect_loc = dict(proximal=['soma'], distal=['soma']) @@ -102,7 +101,4 @@ def __init__(self, pos, gid=None): # Note: Cell properties are set in BasketSingle() BasketSingle.__init__(self, pos, cell_name='L5Basket', gid=gid) self.celltype = 'L5_basket' - - self._synapse_create() - self.set_biophysics() self.sect_loc = dict(proximal=['soma'], distal=[]) From af0f6ed2e1741e6256af6b381afa2440b0faab9b Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 01:02:33 -0400 Subject: [PATCH 04/49] More compact? --- hnn_core/pyramidal.py | 51 +++++++++++-------------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index 42cbb2a25..209a4c724 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -383,33 +383,26 @@ def set_biophysics(self, p_all): self.soma.gl_hh2 = p_all['L5Pyr_soma_gl_hh2'] self.soma.el_hh2 = p_all['L5Pyr_soma_el_hh2'] - # insert 'ca' mechanism - # Units: pS/um^2 - self.soma.insert('ca') - self.soma.gbar_ca = p_all['L5Pyr_soma_gbar_ca'] - # insert 'cad' mechanism # units of tau are ms self.soma.insert('cad') self.soma.taur_cad = p_all['L5Pyr_soma_taur_cad'] - # insert 'kca' mechanism - # units are S/cm^2? - self.soma.insert('kca') - self.soma.gbar_kca = p_all['L5Pyr_soma_gbar_kca'] - - # Insert 'km' mechanism - # Units: pS/um^2 - self.soma.insert('km') - self.soma.gbar_km = p_all['L5Pyr_soma_gbar_km'] + self.soma.insert('ar') + self.soma.taur_cad = p_all['L5Pyr_soma_gbar_ar'] - # insert 'cat' mechanism - self.soma.insert('cat') - self.soma.gbar_cat = p_all['L5Pyr_soma_gbar_cat'] + mechanisms = ['ca', 'kca', 'km', 'cat'] + # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', '??'] + for mechanism in mechanisms: + self.soma.insert(mechanism) + attr = f'gbar_{mechanism}' + setattr(self.soma, attr, p_all[f'L5Pyr_soma_{attr}']) - # insert 'ar' mechanism - self.soma.insert('ar') - self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] + for sec in self.dends.values(): + for mechanism in mechanisms: + sec.insert(mechanism) + attr = f'gbar_{mechanism}' + setattr(sec, attr, p_all[f'L5Pyr_dend_{attr}']) # set dend biophysics not specified in Pyr() for key in self.dends: @@ -420,28 +413,10 @@ def set_biophysics(self, p_all): self.dends[key].gnabar_hh2 = p_all['L5Pyr_dend_gnabar_hh2'] self.dends[key].el_hh2 = p_all['L5Pyr_dend_el_hh2'] - # Insert 'ca' mechanims - # Units: pS/um^2 - self.dends[key].insert('ca') - self.dends[key].gbar_ca = p_all['L5Pyr_dend_gbar_ca'] - # Insert 'cad' mechanism self.dends[key].insert('cad') self.dends[key].taur_cad = p_all['L5Pyr_dend_taur_cad'] - # Insert 'kca' mechanism - self.dends[key].insert('kca') - self.dends[key].gbar_kca = p_all['L5Pyr_dend_gbar_kca'] - - # Insert 'km' mechansim - # Units: pS/um^2 - self.dends[key].insert('km') - self.dends[key].gbar_km = p_all['L5Pyr_dend_gbar_km'] - - # insert 'cat' mechanism - self.dends[key].insert('cat') - self.dends[key].gbar_cat = p_all['L5Pyr_dend_gbar_cat'] - # insert 'ar' mechanism self.dends[key].insert('ar') From 460161e9f4045641a8ec5c84503bb79a9f003b18 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 01:34:36 -0400 Subject: [PATCH 05/49] WIP: improvement or not? --- hnn_core/pyramidal.py | 99 ++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index 209a4c724..d8c8548a0 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -286,37 +286,21 @@ def secs(self): def set_biophysics(self, p_all): """Adds biophysics to soma.""" - # Insert 'hh2' mechanism - self.soma.insert('hh2') - self.soma.gkbar_hh2 = p_all['L2Pyr_soma_gkbar_hh2'] - self.soma.gl_hh2 = p_all['L2Pyr_soma_gl_hh2'] - self.soma.el_hh2 = p_all['L2Pyr_soma_el_hh2'] - self.soma.gnabar_hh2 = p_all['L2Pyr_soma_gnabar_hh2'] - - # Insert 'km' mechanism - # Units: pS/um^2 - self.soma.insert('km') - self.soma.gbar_km = p_all['L2Pyr_soma_gbar_km'] - - # set dend biophysics - # iterate over keys in self.dends and set biophysics for each dend - for key in self.dends: - # neuron syntax is used to set values for mechanisms - # sec.gbar_mech = x sets value of gbar for mech to x for all segs - # in a section. This method is significantly faster than using - # a for loop to iterate over all segments to set mech values - - # Insert 'hh' mechanism - self.dends[key].insert('hh2') - self.dends[key].gkbar_hh2 = p_all['L2Pyr_dend_gkbar_hh2'] - self.dends[key].gl_hh2 = p_all['L2Pyr_dend_gl_hh2'] - self.dends[key].gnabar_hh2 = p_all['L2Pyr_dend_gnabar_hh2'] - self.dends[key].el_hh2 = p_all['L2Pyr_dend_el_hh2'] - - # Insert 'km' mechanism - # Units: pS/um^2 - self.dends[key].insert('km') - self.dends[key].gbar_km = p_all['L2Pyr_dend_gbar_km'] + mechanisms = {'km': ['gbar_km'], + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2']} + + # neuron syntax is used to set values for mechanisms + # sec.gbar_mech = x sets value of gbar for mech to x for all segs + # in a section. This method is significantly faster than using + # a for loop to iterate over all segments to set mech values + for sec in self.get_sections(): + sec_name = sec.name().split('_', 1)[1] + sec_name = 'soma' if sec_name == 'soma' else 'dend' + for key, attrs in mechanisms.items(): + sec.insert(key) + for attr in attrs: + setattr(sec, attr, p_all[f'L2Pyr_{sec_name}_{attr}']) # Units for e: mV @@ -376,47 +360,28 @@ def _get_soma_props(self, pos, p_all): def set_biophysics(self, p_all): "Set the biophysics for the default Pyramidal cell." - # Insert 'hh2' mechanism - self.soma.insert('hh2') - self.soma.gkbar_hh2 = p_all['L5Pyr_soma_gkbar_hh2'] - self.soma.gnabar_hh2 = p_all['L5Pyr_soma_gnabar_hh2'] - self.soma.gl_hh2 = p_all['L5Pyr_soma_gl_hh2'] - self.soma.el_hh2 = p_all['L5Pyr_soma_el_hh2'] - - # insert 'cad' mechanism - # units of tau are ms - self.soma.insert('cad') - self.soma.taur_cad = p_all['L5Pyr_soma_taur_cad'] + mechanisms = {'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2'], + 'ca': ['gbar_ca'], + 'cad': ['taur_cad'], + 'kca': ['gbar_kca'], + 'km': ['gbar_km'], + 'cat': ['gbar_cat']} + + # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] + for sec in self.get_sections(): + sec_name = sec.name().split('_', 1)[1] + sec_name = 'soma' if sec_name == 'soma' else 'dend' + for key, attrs in mechanisms.items(): + sec.insert(key) + for attr in attrs: + setattr(sec, attr, p_all[f'L5Pyr_{sec_name}_{attr}']) self.soma.insert('ar') - self.soma.taur_cad = p_all['L5Pyr_soma_gbar_ar'] - - mechanisms = ['ca', 'kca', 'km', 'cat'] - # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', '??'] - for mechanism in mechanisms: - self.soma.insert(mechanism) - attr = f'gbar_{mechanism}' - setattr(self.soma, attr, p_all[f'L5Pyr_soma_{attr}']) - - for sec in self.dends.values(): - for mechanism in mechanisms: - sec.insert(mechanism) - attr = f'gbar_{mechanism}' - setattr(sec, attr, p_all[f'L5Pyr_dend_{attr}']) + self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] # set dend biophysics not specified in Pyr() for key in self.dends: - # Insert 'hh2' mechanism - self.dends[key].insert('hh2') - self.dends[key].gkbar_hh2 = p_all['L5Pyr_dend_gkbar_hh2'] - self.dends[key].gl_hh2 = p_all['L5Pyr_dend_gl_hh2'] - self.dends[key].gnabar_hh2 = p_all['L5Pyr_dend_gnabar_hh2'] - self.dends[key].el_hh2 = p_all['L5Pyr_dend_el_hh2'] - - # Insert 'cad' mechanism - self.dends[key].insert('cad') - self.dends[key].taur_cad = p_all['L5Pyr_dend_taur_cad'] - # insert 'ar' mechanism self.dends[key].insert('ar') From b34bb1c3aeafd6a9c8484dddcc7d2d8e68c07e21 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 08:15:45 -0400 Subject: [PATCH 06/49] MAINT: make class thinner --- hnn_core/pyramidal.py | 88 ++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index d8c8548a0..6c7fea769 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -18,7 +18,7 @@ # Units for gbar: S/cm^2 unless otherwise noted -def _flat_to_nested(params, prefix, level1_keys, level2_keys): +def _flat_to_nested(params, cell_type, level1_keys, level2_keys): """Convert a flat dictionary to a nested dictionary.""" nested_dict = dict() for level1_key in level1_keys: @@ -29,11 +29,48 @@ def _flat_to_nested(params, prefix, level1_keys, level2_keys): else: # map apicaltrunk -> apical_trunk etc. middle = level1_key.replace('_', '') - level2_dict[key] = params[f'{prefix}_{middle}_{key}'] + level2_dict[key] = params[f'{cell_type}_{middle}_{key}'] nested_dict[level1_key] = level2_dict return nested_dict +def _get_soma_props(p_all, cell_type, pos): + """Hardcoded somatic properties.""" + return { + 'pos': pos, + 'L': p_all[f'{cell_type}_soma_L'], + 'diam': p_all[f'{cell_type}_soma_diam'], + 'cm': p_all[f'{cell_type}_soma_cm'], + 'Ra': p_all[f'{cell_type}_soma_Ra'], + 'name': cell_type, + } + + +def _get_syn_props(p_all, cell_type): + return { + 'ampa': { + 'e': p_all['%s_ampa_e' % cell_type], + 'tau1': p_all['%s_ampa_tau1' % cell_type], + 'tau2': p_all['%s_ampa_tau2' % cell_type], + }, + 'nmda': { + 'e': p_all['%s_nmda_e' % cell_type], + 'tau1': p_all['%s_nmda_tau1' % cell_type], + 'tau2': p_all['%s_nmda_tau2' % cell_type], + }, + 'gabaa': { + 'e': p_all['%s_gabaa_e' % cell_type], + 'tau1': p_all['%s_gabaa_tau1' % cell_type], + 'tau2': p_all['%s_gabaa_tau2' % cell_type], + }, + 'gabab': { + 'e': p_all['%s_gabab_e' % cell_type], + 'tau1': p_all['%s_gabab_tau1' % cell_type], + 'tau2': p_all['%s_gabab_tau2' % cell_type], + } + } + + class Pyr(_Cell): """Pyramidal neuron. @@ -83,12 +120,14 @@ def __init__(self, pos, celltype, override_params=None, gid=None): p_all = compare_dictionaries(p_all_default, override_params) # Get somatic, dendritic, and synapse properties - soma_props = self._get_soma_props(pos, p_all) + if celltype == 'L5_pyramidal': + self.name = 'L5Pyr' + else: + self.name = 'L2Pyr' + soma_props = _get_soma_props(p_all, self.name, pos) _Cell.__init__(self, soma_props, gid=gid) self.create_soma() - # store cell_name as self variable for later use - self.name = soma_props['name'] # preallocate dict to store dends self.dends = {} self.synapses = dict() @@ -98,10 +137,10 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.celltype = celltype level2_keys = ['L', 'diam', 'Ra', 'cm'] - p_dend = _flat_to_nested(p_all, prefix=self.name, + p_dend = _flat_to_nested(p_all, cell_type=self.name, level1_keys=self.section_names(), level2_keys=level2_keys) - p_syn = self._get_syn_props(p_all) + p_syn = _get_syn_props(p_all, self.name) # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra @@ -177,30 +216,6 @@ def get_sections(self): ls.append(self.dends[key]) return ls - def _get_syn_props(self, p_all): - return { - 'ampa': { - 'e': p_all['%s_ampa_e' % self.name], - 'tau1': p_all['%s_ampa_tau1' % self.name], - 'tau2': p_all['%s_ampa_tau2' % self.name], - }, - 'nmda': { - 'e': p_all['%s_nmda_e' % self.name], - 'tau1': p_all['%s_nmda_tau1' % self.name], - 'tau2': p_all['%s_nmda_tau2' % self.name], - }, - 'gabaa': { - 'e': p_all['%s_gabaa_e' % self.name], - 'tau1': p_all['%s_gabaa_tau1' % self.name], - 'tau2': p_all['%s_gabaa_tau2' % self.name], - }, - 'gabab': { - 'e': p_all['%s_gabab_e' % self.name], - 'tau1': p_all['%s_gabab_tau1' % self.name], - 'tau2': p_all['%s_gabab_tau2' % self.name], - } - } - def _synapse_create(self, p_syn): """Creates synapses onto this cell.""" # Somatic synapses @@ -269,17 +284,6 @@ def section_names(self): return ['apical_trunk', 'apical_1', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - def _get_soma_props(self, pos, p_all): - """Hardcoded somatic properties.""" - return { - 'pos': pos, - 'L': p_all['L2Pyr_soma_L'], - 'diam': p_all['L2Pyr_soma_diam'], - 'cm': p_all['L2Pyr_soma_cm'], - 'Ra': p_all['L2Pyr_soma_Ra'], - 'name': 'L2Pyr', - } - def secs(self): return _secs_L2Pyr() From 1c08f18f91bc80e07a1611565cbba2ba7f744a58 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 08:21:18 -0400 Subject: [PATCH 07/49] MAINT: simplify --- hnn_core/pyramidal.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index 6c7fea769..8583fd346 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -202,19 +202,12 @@ def create_dends(self, p_dend_props): name=self.name + '_' + key) # create dend # apical: 0--4; basal: 5--7 self.list_dend = [self.dends[key] for key in - ['apical_trunk', 'apical_oblique', 'apical_1', - 'apical_2', 'apical_tuft', 'basal_1', 'basal_2', - 'basal_3'] if key in self.dends] + self.section_names() if key in self.dends] self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] self.sect_loc['distal'] = ['apicaltuft'] def get_sections(self): - ls = [self.soma] - for key in ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3']: - if key in self.dends: - ls.append(self.dends[key]) - return ls + return [self.soma] + list(self.dends.values()) def _synapse_create(self, p_syn): """Creates synapses onto this cell.""" From 37bd4a66757e2c33f09e042ffa6b41e912dcfd22 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 08:37:42 -0400 Subject: [PATCH 08/49] ENH: simplify more --- hnn_core/pyramidal.py | 39 +++++---------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index 8583fd346..0c9c8e4eb 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -218,29 +218,11 @@ def _synapse_create(self, p_syn): **p_syn['gabab']) # Dendritic synapses - self.synapses['apicaloblique_ampa'] = self.syn_create( - self.dends['apical_oblique'](0.5), **p_syn['ampa']) - self.synapses['apicaloblique_nmda'] = self.syn_create( - self.dends['apical_oblique'](0.5), **p_syn['nmda']) - - self.synapses['basal2_ampa'] = self.syn_create( - self.dends['basal_2'](0.5), **p_syn['ampa']) - self.synapses['basal2_nmda'] = self.syn_create( - self.dends['basal_2'](0.5), **p_syn['nmda']) - - self.synapses['basal3_ampa'] = self.syn_create( - self.dends['basal_3'](0.5), **p_syn['ampa']) - self.synapses['basal3_nmda'] = self.syn_create( - self.dends['basal_3'](0.5), **p_syn['nmda']) - - self.synapses['apicaltuft_ampa'] = self.syn_create( - self.dends['apical_tuft'](0.5), **p_syn['ampa']) - self.synapses['apicaltuft_nmda'] = self.syn_create( - self.dends['apical_tuft'](0.5), **p_syn['nmda']) - - if self.name == 'L5Pyr': - self.synapses['apicaltuft_gabaa'] = self.syn_create( - self.dends['apical_tuft'](0.5), **p_syn['gabaa']) + for sec in self.section_names(): + for receptor in p_syn: + syn_key = sec.replace('_', '') + '_' + receptor + self.synapses[syn_key] = self.syn_create( + self.dends[sec](0.5), **p_syn[receptor]) class L2Pyr(Pyr): @@ -343,17 +325,6 @@ def section_names(self): def secs(self): return _secs_L5Pyr() - def _get_soma_props(self, pos, p_all): - """Sets somatic properties. Returns dictionary.""" - return { - 'pos': pos, - 'L': p_all['L5Pyr_soma_L'], - 'diam': p_all['L5Pyr_soma_diam'], - 'cm': p_all['L5Pyr_soma_cm'], - 'Ra': p_all['L5Pyr_soma_Ra'], - 'name': 'L5Pyr', - } - def set_biophysics(self, p_all): "Set the biophysics for the default Pyramidal cell." From a0c81eb3040ad4fccf780762b5c08e6a140bcf61 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 08:50:45 -0400 Subject: [PATCH 09/49] MAINT: no need of list_dend --- hnn_core/cell.py | 3 +-- hnn_core/pyramidal.py | 12 +----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index b40c9c44e..980084bcd 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -108,7 +108,6 @@ def __init__(self, soma_props, gid=None): # variable for the list_IClamp self.list_IClamp = None self.soma_props = soma_props - self.list_dend = list() self.dends = list() self.create_soma() self.rec_v = h.Vector() @@ -369,7 +368,7 @@ def set_geometry(self): # for height and xz plane for depth. This is opposite for model as a # whole, but convention is followed in this function ease use of gui. sec_pts, sec_lens, sec_diams, _, topology = self.secs() - for sec in [self.soma] + self.list_dend: + for sec in self.get_sections(): h.pt3dclear(sec=sec) sec_name = sec.name().split('_', 1)[1] for pt in sec_pts[sec_name]: diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index 0c9c8e4eb..da771aee8 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -91,8 +91,6 @@ class Pyr(_Cell): ---------- name : str The name of the cell, 'L5Pyr' or 'L2Pyr' - list_dend : list of str - List of dendrites. sect_loc : dict of list Can have keys 'proximal' or 'distal' each containing names of section locations that are proximal or distal. @@ -132,8 +130,6 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.dends = {} self.synapses = dict() self.sect_loc = dict() - # for legacy use with L5Pyr - self.list_dend = [] self.celltype = celltype level2_keys = ['L', 'diam', 'Ra', 'cm'] @@ -200,9 +196,7 @@ def create_dends(self, p_dend_props): for key in p_dend_props: self.dends[key] = h.Section( name=self.name + '_' + key) # create dend - # apical: 0--4; basal: 5--7 - self.list_dend = [self.dends[key] for key in - self.section_names() if key in self.dends] + self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] self.sect_loc['distal'] = ['apicaltuft'] @@ -243,8 +237,6 @@ class L2Pyr(Pyr): ---------- name : str The name of the cell - list_dend : list of str - List of dendrites. dends : dict The dendrites. The key is the name of the dendrite and the value is an instance of h.Section. @@ -304,8 +296,6 @@ class L5Pyr(Pyr): ---------- name : str The name of the cell - list_dend : list of str - List of dendrites. dends : dict The dendrites. The key is the name of the dendrite and the value is an instance of h.Section. From ff76f20fb740590ccc908f4695afc40e482449c4 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 09:37:32 -0400 Subject: [PATCH 10/49] ENH: set_biophysics in parent class --- hnn_core/cell.py | 29 ++++++++++++++- hnn_core/pyramidal.py | 82 ++++++++++++------------------------------- 2 files changed, 50 insertions(+), 61 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 980084bcd..48baac3d7 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -88,6 +88,11 @@ class _Cell(ABC): ---------- pos : list of length 3 The position of the cell. + dends : dict + The dendrites. The key is the name of the dendrite + and the value is an instance of h.Section. + synapses : dict + The synapses that the cell can use for connections. dipole_pp : list of h.Dipole() The Dipole objects (see dipole.mod). rec_v : h.Vector() @@ -108,10 +113,15 @@ def __init__(self, soma_props, gid=None): # variable for the list_IClamp self.list_IClamp = None self.soma_props = soma_props - self.dends = list() + # preallocate dict to store dends + self.dends = dict() + self.synapses = dict() + self.sect_loc = dict() self.create_soma() self.rec_v = h.Vector() self.rec_i = dict() + # insert iclamp + self.list_IClamp = [] self._gid = None self.tonic_biases = list() if gid is not None: @@ -143,6 +153,23 @@ def get_sections(self): """Get sections in a cell.""" pass + def set_biophysics(self, p_all): + "Set the biophysics for the default Pyramidal cell." + + # neuron syntax is used to set values for mechanisms + # sec.gbar_mech = x sets value of gbar for mech to x for all segs + # in a section. This method is significantly faster than using + # a for loop to iterate over all segments to set mech values + + # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] + for sec in self.get_sections(): + sec_name = sec.name().split('_', 1)[1] + sec_name = 'soma' if sec_name == 'soma' else 'dend' + for key, attrs in self.mechanisms.items(): + sec.insert(key) + for attr in attrs: + setattr(sec, attr, p_all[f'{self.name}_{sec_name}_{attr}']) + def create_soma(self): """Create soma and set geometry.""" # make L_soma and diam_soma elements of self diff --git a/hnn_core/pyramidal.py b/hnn_core/pyramidal.py index da771aee8..2fd65c047 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/pyramidal.py @@ -96,9 +96,6 @@ class Pyr(_Cell): names of section locations that are proximal or distal. celltype : str The cell type, 'L5_Pyramidal' or 'L2_Pyramidal' - dends : dict - The dendrites. The key is the name of the dendrite - and the value is an instance of h.Section. synapses : dict The synapses that the cell can use for connections. """ @@ -126,10 +123,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): _Cell.__init__(self, soma_props, gid=gid) self.create_soma() - # preallocate dict to store dends - self.dends = {} - self.synapses = dict() - self.sect_loc = dict() + self.celltype = celltype level2_keys = ['L', 'diam', 'Ra', 'cm'] @@ -141,10 +135,30 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra self.create_dends(p_dend) # just creates the sections + + self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] + self.sect_loc['distal'] = ['apicaltuft'] + # sets geom properties; adjusted after translation from # hoc (2009 model) self.set_geometry(p_dend) + if celltype == 'L5_pyramidal': + self.mechanisms = { + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2'], + 'ca': ['gbar_ca'], + 'cad': ['taur_cad'], + 'kca': ['gbar_kca'], + 'km': ['gbar_km'], + 'cat': ['gbar_cat'] + } + else: + self.mechanisms = { + 'km': ['gbar_km'], + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2']} + # biophysics self.set_biophysics(p_all) @@ -155,9 +169,6 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # create synapses self._synapse_create(p_syn) - # insert iclamp - self.list_IClamp = [] - def set_geometry(self, p_dend): """Define shape of the neuron and connect sections. @@ -197,9 +208,6 @@ def create_dends(self, p_dend_props): self.dends[key] = h.Section( name=self.name + '_' + key) # create dend - self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] - self.sect_loc['distal'] = ['apicaltuft'] - def get_sections(self): return [self.soma] + list(self.dends.values()) @@ -237,11 +245,6 @@ class L2Pyr(Pyr): ---------- name : str The name of the cell - dends : dict - The dendrites. The key is the name of the dendrite - and the value is an instance of h.Section. - synapses : dict - The synapses that the cell can use for connections. """ def __init__(self, pos=None, override_params=None, gid=None): @@ -254,26 +257,6 @@ def section_names(self): def secs(self): return _secs_L2Pyr() - def set_biophysics(self, p_all): - """Adds biophysics to soma.""" - - mechanisms = {'km': ['gbar_km'], - 'hh2': ['gkbar_hh2', 'gnabar_hh2', - 'gl_hh2', 'el_hh2']} - - # neuron syntax is used to set values for mechanisms - # sec.gbar_mech = x sets value of gbar for mech to x for all segs - # in a section. This method is significantly faster than using - # a for loop to iterate over all segments to set mech values - for sec in self.get_sections(): - sec_name = sec.name().split('_', 1)[1] - sec_name = 'soma' if sec_name == 'soma' else 'dend' - for key, attrs in mechanisms.items(): - sec.insert(key) - for attr in attrs: - setattr(sec, attr, p_all[f'L2Pyr_{sec_name}_{attr}']) - - # Units for e: mV # Units for gbar: S/cm^2 unless otherwise noted # units for taur: ms @@ -296,11 +279,6 @@ class L5Pyr(Pyr): ---------- name : str The name of the cell - dends : dict - The dendrites. The key is the name of the dendrite - and the value is an instance of h.Section. - synapses : dict - The synapses that the cell can use for connections. """ def __init__(self, pos=None, override_params=None, gid=None): @@ -317,23 +295,7 @@ def secs(self): def set_biophysics(self, p_all): "Set the biophysics for the default Pyramidal cell." - - mechanisms = {'hh2': ['gkbar_hh2', 'gnabar_hh2', - 'gl_hh2', 'el_hh2'], - 'ca': ['gbar_ca'], - 'cad': ['taur_cad'], - 'kca': ['gbar_kca'], - 'km': ['gbar_km'], - 'cat': ['gbar_cat']} - - # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] - for sec in self.get_sections(): - sec_name = sec.name().split('_', 1)[1] - sec_name = 'soma' if sec_name == 'soma' else 'dend' - for key, attrs in mechanisms.items(): - sec.insert(key) - for attr in attrs: - setattr(sec, attr, p_all[f'L5Pyr_{sec_name}_{attr}']) + _Cell.set_biophysics(self, p_all) self.soma.insert('ar') self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] From ad489276c6a8d029aba4a788cbb4e2b6852dd52c Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 09:48:22 -0400 Subject: [PATCH 11/49] MAINT: move cell classes to one file --- hnn_core/__init__.py | 3 +- hnn_core/basket.py | 104 --------------------- hnn_core/{pyramidal.py => cell_classes.py} | 98 ++++++++++++++++++- hnn_core/network_builder.py | 3 +- 4 files changed, 99 insertions(+), 109 deletions(-) delete mode 100644 hnn_core/basket.py rename hnn_core/{pyramidal.py => cell_classes.py} (78%) diff --git a/hnn_core/__init__.py b/hnn_core/__init__.py index 7230b08b4..db36a1a7d 100644 --- a/hnn_core/__init__.py +++ b/hnn_core/__init__.py @@ -3,8 +3,7 @@ from .params import Params, read_params from .network import Network from .cell_response import CellResponse, read_spikes -from .pyramidal import L2Pyr, L5Pyr -from .basket import L2Basket, L5Basket +from .cell_classes import L2Pyr, L5Pyr, L2Basket, L5Basket from .parallel_backends import MPIBackend, JoblibBackend __version__ = '0.2.dev0' diff --git a/hnn_core/basket.py b/hnn_core/basket.py deleted file mode 100644 index 5ea501edd..000000000 --- a/hnn_core/basket.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Model for inhibitory cell class.""" - -# Authors: Mainak Jas -# Sam Neymotin - -from .cell import _Cell -from .params_default import _secs_Basket - -# Units for e: mV -# Units for gbar: S/cm^2 unless otherwise noted - - -class BasketSingle(_Cell): - """Inhibitory cell class. - - Attributes - ---------- - synapses : dict - The synapses that the cell can use for connections. - sect_loc : dict of list - Can have keys 'proximal' or 'distal' each containing - names of section locations that are proximal or distal. - """ - - def __init__(self, pos, cell_name='Basket', gid=None): - self.props = self._get_soma_props(cell_name, pos) - _Cell.__init__(self, self.props, gid=gid) - # store cell name for later - self.name = cell_name - self.set_geometry() - self.synapses = dict() - self._synapse_create() - self.set_biophysics() - - def set_biophysics(self): - self.soma.insert('hh2') - - def _get_soma_props(self, cell_name, pos): - return { - 'pos': pos, - 'L': 39., - 'diam': 20., - 'cm': 0.85, - 'Ra': 200., - 'name': cell_name, - } - - def secs(self): - return _secs_Basket() - - def get_sections(self): - """Get sections.""" - return [self.soma] - - # creation of synapses - def _synapse_create(self): - # creates synapses onto this cell - self.synapses['soma_ampa'] = self.syn_create( - self.soma(0.5), e=0., tau1=0.5, tau2=5.) - self.synapses['soma_gabaa'] = self.syn_create( - self.soma(0.5), e=-80, tau1=0.5, tau2=5.) - self.synapses['soma_nmda'] = self.syn_create( - self.soma(0.5), e=0., tau1=1., tau2=20.) - - -class L2Basket(BasketSingle): - """Class for layer 2 basket cells. - - Parameters - ---------- - pos : tuple - Coordinates of cell soma in xyz-space - gid : int or None (optional) - Each cell in a network is uniquely identified by it's "global ID": GID. - The GID is an integer from 0 to n_cells, or None if the cell is not - yet attached to a network. Once the GID is set, it cannot be changed. - """ - - def __init__(self, pos, gid=None): - # BasketSingle.__init__(self, pos, L, diam, Ra, cm) - # Note: Basket cell properties set in BasketSingle()) - BasketSingle.__init__(self, pos, cell_name='L2Basket', gid=gid) - self.celltype = 'L2_basket' - self.sect_loc = dict(proximal=['soma'], distal=['soma']) - - -class L5Basket(BasketSingle): - """Class for layer 5 basket cells. - - Parameters - ---------- - pos : tuple - Coordinates of cell soma in xyz-space - gid : int or None (optional) - Each cell in a network is uniquely identified by it's "global ID": GID. - The GID is an integer from 0 to n_cells, or None if the cell is not - yet attached to a network. Once the GID is set, it cannot be changed. - """ - - def __init__(self, pos, gid=None): - # Note: Cell properties are set in BasketSingle() - BasketSingle.__init__(self, pos, cell_name='L5Basket', gid=gid) - self.celltype = 'L5_basket' - self.sect_loc = dict(proximal=['soma'], distal=[]) diff --git a/hnn_core/pyramidal.py b/hnn_core/cell_classes.py similarity index 78% rename from hnn_core/pyramidal.py rename to hnn_core/cell_classes.py index 2fd65c047..965a22013 100644 --- a/hnn_core/pyramidal.py +++ b/hnn_core/cell_classes.py @@ -12,7 +12,8 @@ from .params import compare_dictionaries from .params_default import (get_L2Pyr_params_default, get_L5Pyr_params_default, - _secs_L2Pyr, _secs_L5Pyr) + _secs_L2Pyr, _secs_L5Pyr, + _secs_Basket) # Units for e: mV # Units for gbar: S/cm^2 unless otherwise noted @@ -46,6 +47,17 @@ def _get_soma_props(p_all, cell_type, pos): } +def _get_basket_soma_props(cell_name, pos): + return { + 'pos': pos, + 'L': 39., + 'diam': 20., + 'cm': 0.85, + 'Ra': 200., + 'name': cell_name, + } + + def _get_syn_props(p_all, cell_type): return { 'ampa': { @@ -71,6 +83,90 @@ def _get_syn_props(p_all, cell_type): } +class BasketSingle(_Cell): + """Inhibitory cell class. + + Attributes + ---------- + synapses : dict + The synapses that the cell can use for connections. + sect_loc : dict of list + Can have keys 'proximal' or 'distal' each containing + names of section locations that are proximal or distal. + """ + + def __init__(self, pos, cell_name='Basket', gid=None): + self.props = _get_basket_soma_props(cell_name, pos) + _Cell.__init__(self, self.props, gid=gid) + # store cell name for later + self.name = cell_name + self.set_geometry() + self.synapses = dict() + self._synapse_create() + self.set_biophysics() + + def set_biophysics(self): + self.soma.insert('hh2') + + def secs(self): + return _secs_Basket() + + def get_sections(self): + """Get sections.""" + return [self.soma] + + # creation of synapses + def _synapse_create(self): + # creates synapses onto this cell + self.synapses['soma_ampa'] = self.syn_create( + self.soma(0.5), e=0., tau1=0.5, tau2=5.) + self.synapses['soma_gabaa'] = self.syn_create( + self.soma(0.5), e=-80, tau1=0.5, tau2=5.) + self.synapses['soma_nmda'] = self.syn_create( + self.soma(0.5), e=0., tau1=1., tau2=20.) + + +class L2Basket(BasketSingle): + """Class for layer 2 basket cells. + + Parameters + ---------- + pos : tuple + Coordinates of cell soma in xyz-space + gid : int or None (optional) + Each cell in a network is uniquely identified by it's "global ID": GID. + The GID is an integer from 0 to n_cells, or None if the cell is not + yet attached to a network. Once the GID is set, it cannot be changed. + """ + + def __init__(self, pos, gid=None): + # BasketSingle.__init__(self, pos, L, diam, Ra, cm) + # Note: Basket cell properties set in BasketSingle()) + BasketSingle.__init__(self, pos, cell_name='L2Basket', gid=gid) + self.celltype = 'L2_basket' + self.sect_loc = dict(proximal=['soma'], distal=['soma']) + + +class L5Basket(BasketSingle): + """Class for layer 5 basket cells. + + Parameters + ---------- + pos : tuple + Coordinates of cell soma in xyz-space + gid : int or None (optional) + Each cell in a network is uniquely identified by it's "global ID": GID. + The GID is an integer from 0 to n_cells, or None if the cell is not + yet attached to a network. Once the GID is set, it cannot be changed. + """ + + def __init__(self, pos, gid=None): + # Note: Cell properties are set in BasketSingle() + BasketSingle.__init__(self, pos, cell_name='L5Basket', gid=gid) + self.celltype = 'L5_basket' + self.sect_loc = dict(proximal=['soma'], distal=[]) + + class Pyr(_Cell): """Pyramidal neuron. diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index c371c6e53..8f58589f5 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -13,8 +13,7 @@ h.nrnunit_use_legacy(1) from .cell import _ArtificialCell -from .pyramidal import L2Pyr, L5Pyr -from .basket import L2Basket, L5Basket +from .cell_classes import L2Pyr, L5Pyr, L2Basket, L5Basket from .params import _long_name, _short_name from copy import deepcopy From 6f2a43b248ce2caa5ded3ed244fc845e48737263 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 18:17:51 -0400 Subject: [PATCH 12/49] ENH: Remove superfluous celltype --- hnn_core/cell_classes.py | 8 -------- hnn_core/network_builder.py | 12 ++++++------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 965a22013..ccaca3b58 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -143,7 +143,6 @@ def __init__(self, pos, gid=None): # BasketSingle.__init__(self, pos, L, diam, Ra, cm) # Note: Basket cell properties set in BasketSingle()) BasketSingle.__init__(self, pos, cell_name='L2Basket', gid=gid) - self.celltype = 'L2_basket' self.sect_loc = dict(proximal=['soma'], distal=['soma']) @@ -163,7 +162,6 @@ class L5Basket(BasketSingle): def __init__(self, pos, gid=None): # Note: Cell properties are set in BasketSingle() BasketSingle.__init__(self, pos, cell_name='L5Basket', gid=gid) - self.celltype = 'L5_basket' self.sect_loc = dict(proximal=['soma'], distal=[]) @@ -174,8 +172,6 @@ class Pyr(_Cell): ---------- pos : tuple Coordinates of cell soma in xyz-space - celltype : str - Either 'L2_Pyramidal' or 'L5_Pyramidal' override_params : dict or None (optional) Parameters specific to L2 pyramidal neurons to override the default set gid : int or None (optional) @@ -190,8 +186,6 @@ class Pyr(_Cell): sect_loc : dict of list Can have keys 'proximal' or 'distal' each containing names of section locations that are proximal or distal. - celltype : str - The cell type, 'L5_Pyramidal' or 'L2_Pyramidal' synapses : dict The synapses that the cell can use for connections. """ @@ -220,8 +214,6 @@ def __init__(self, pos, celltype, override_params=None, gid=None): _Cell.__init__(self, soma_props, gid=gid) self.create_soma() - self.celltype = celltype - level2_keys = ['L', 'diam', 'Ra', 'cm'] p_dend = _flat_to_nested(p_all, cell_type=self.name, level1_keys=self.section_names(), diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 8f58589f5..d5d2819e6 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -489,8 +489,8 @@ def _record_spikes(self): def aggregate_data(self): """Aggregate somatic currents, voltages, and dipoles.""" for cell in self.cells: - if cell.celltype in ('L5_pyramidal', 'L2_pyramidal'): - self.dipoles[cell.celltype].add(cell.dipole) + if cell.name in ('L5Pyr', 'L2Pyr'): + self.dipoles[_long_name(cell.name)].add(cell.dipole) self._vsoma[cell.gid] = cell.rec_v self._isoma[cell.gid] = cell.rec_i @@ -503,9 +503,9 @@ def state_init(self): seclist.wholetree(sec=cell.soma) for sect in seclist: for seg in sect: - if cell.celltype == 'L2_pyramidal': + if cell.name == 'L2Pyr': seg.v = -71.46 - elif cell.celltype == 'L5_pyramidal': + elif cell.name == 'L5Pyr': if sect.name() == 'L5Pyr_apical_1': seg.v = -71.32 elif sect.name() == 'L5Pyr_apical_2': @@ -514,9 +514,9 @@ def state_init(self): seg.v = -67.30 else: seg.v = -72. - elif cell.celltype == 'L2_basket': + elif cell.name == 'L2Basket': seg.v = -64.9737 - elif cell.celltype == 'L5_basket': + elif cell.name == 'L5Basket': seg.v = -64.9737 def move_cells_to_pos(self): From 49c4b96de7b2cc5921f671286bec16d0a67c85f7 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 18:26:33 -0400 Subject: [PATCH 13/49] self.L and self.diam not needed --- hnn_core/cell.py | 2 -- hnn_core/cell_classes.py | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 48baac3d7..96cb3d683 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -176,8 +176,6 @@ def create_soma(self): # Used in shape_change() b/c func clobbers self.soma.L, self.soma.diam soma_props = self.soma_props - self.L = soma_props['L'] - self.diam = soma_props['diam'] self.pos = soma_props['pos'] self.soma = h.Section(cell=self, name=soma_props['name'] + '_soma') diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index ccaca3b58..2bc7aac94 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -17,6 +17,7 @@ # Units for e: mV # Units for gbar: S/cm^2 unless otherwise noted +# units for taur: ms def _flat_to_nested(params, cell_type, level1_keys, level2_keys): @@ -345,9 +346,6 @@ def section_names(self): def secs(self): return _secs_L2Pyr() -# Units for e: mV -# Units for gbar: S/cm^2 unless otherwise noted -# units for taur: ms class L5Pyr(Pyr): """Layer 5 Pyramidal class. From 5952a1b5a6055a42007641618132690f0e1cfd8d Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 18:45:34 -0400 Subject: [PATCH 14/49] MAINT: l2_basket and l5_basket into func --- hnn_core/__init__.py | 2 +- hnn_core/cell_classes.py | 38 ++++++++++++------------------------- hnn_core/network_builder.py | 9 +++++---- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/hnn_core/__init__.py b/hnn_core/__init__.py index db36a1a7d..c010fffe7 100644 --- a/hnn_core/__init__.py +++ b/hnn_core/__init__.py @@ -3,7 +3,7 @@ from .params import Params, read_params from .network import Network from .cell_response import CellResponse, read_spikes -from .cell_classes import L2Pyr, L5Pyr, L2Basket, L5Basket +from .cell_classes import L2Pyr, L5Pyr, basket from .parallel_backends import MPIBackend, JoblibBackend __version__ = '0.2.dev0' diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 2bc7aac94..f3d45ca58 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -127,8 +127,8 @@ def _synapse_create(self): self.soma(0.5), e=0., tau1=1., tau2=20.) -class L2Basket(BasketSingle): - """Class for layer 2 basket cells. +def basket(pos, cell_name='L2Basket', gid=None): + """Get layer 2 basket cells. Parameters ---------- @@ -138,32 +138,18 @@ class L2Basket(BasketSingle): Each cell in a network is uniquely identified by it's "global ID": GID. The GID is an integer from 0 to n_cells, or None if the cell is not yet attached to a network. Once the GID is set, it cannot be changed. - """ - - def __init__(self, pos, gid=None): - # BasketSingle.__init__(self, pos, L, diam, Ra, cm) - # Note: Basket cell properties set in BasketSingle()) - BasketSingle.__init__(self, pos, cell_name='L2Basket', gid=gid) - self.sect_loc = dict(proximal=['soma'], distal=['soma']) - - -class L5Basket(BasketSingle): - """Class for layer 5 basket cells. - Parameters - ---------- - pos : tuple - Coordinates of cell soma in xyz-space - gid : int or None (optional) - Each cell in a network is uniquely identified by it's "global ID": GID. - The GID is an integer from 0 to n_cells, or None if the cell is not - yet attached to a network. Once the GID is set, it cannot be changed. + Returns + ------- + cell : instance of BasketSingle + The basket cell. """ - - def __init__(self, pos, gid=None): - # Note: Cell properties are set in BasketSingle() - BasketSingle.__init__(self, pos, cell_name='L5Basket', gid=gid) - self.sect_loc = dict(proximal=['soma'], distal=[]) + cell = BasketSingle(pos, cell_name=cell_name, gid=gid) + if cell_name == 'L2Basket': + cell.sect_loc = dict(proximal=['soma'], distal=['soma']) + elif cell_name == 'L5Basket': + cell.sect_loc = dict(proximal=['soma'], distal=[]) + return cell class Pyr(_Cell): diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index d5d2819e6..7d20958f6 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -13,7 +13,7 @@ h.nrnunit_use_legacy(1) from .cell import _ArtificialCell -from .cell_classes import L2Pyr, L5Pyr, L2Basket, L5Basket +from .cell_classes import L2Pyr, L5Pyr, basket from .params import _long_name, _short_name from copy import deepcopy @@ -377,7 +377,7 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, External inputs are not targets. """ type2class = {'L2_pyramidal': L2Pyr, 'L5_pyramidal': L5Pyr, - 'L2_basket': L2Basket, 'L5_basket': L5Basket} + 'L2_basket': basket, 'L5_basket': basket} # loop through ALL gids # have to loop over self._gid_list, since this is what we got # on this rank (MPI) @@ -395,8 +395,9 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, cell = PyramidalCell(src_pos, override_params=None, gid=gid) else: - BasketCell = type2class[src_type] - cell = BasketCell(src_pos, gid=gid) + basket_cell = type2class[src_type] + cell = basket_cell(src_pos, cell_name=_short_name(src_type), + gid=gid) if ('tonic' in self.net.external_biases and src_type in self.net.external_biases['tonic']): cell.create_tonic_bias(**self.net.external_biases From 22e4a4b15b8ccfebf9e20d8e1f4f174867801f24 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 21:45:56 -0400 Subject: [PATCH 15/49] ENH: remove BasketSingle --- hnn_core/cell.py | 15 ++---- hnn_core/cell_classes.py | 91 +++++++++++++------------------------ hnn_core/tests/test_cell.py | 9 +--- 3 files changed, 38 insertions(+), 77 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 96cb3d683..1a2750238 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -71,7 +71,7 @@ def gid(self, gid): raise RuntimeError('Global ID for this cell already assigned!') -class _Cell(ABC): +class Cell(ABC): """Create a cell object. Parameters @@ -148,11 +148,6 @@ def gid(self, gid): else: raise RuntimeError('Global ID for this cell already assigned!') - @abstractmethod - def get_sections(self): - """Get sections in a cell.""" - pass - def set_biophysics(self, p_all): "Set the biophysics for the default Pyramidal cell." @@ -162,7 +157,7 @@ def set_biophysics(self, p_all): # a for loop to iterate over all segments to set mech values # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] - for sec in self.get_sections(): + for sec in self.sections: sec_name = sec.name().split('_', 1)[1] sec_name = 'soma' if sec_name == 'soma' else 'dend' for key, attrs in self.mechanisms.items(): @@ -193,7 +188,7 @@ def move_to_pos(self): dy = self.pos[2] - y0 dz = self.pos[1] * 100 - z0 - for s in self.get_sections(): + for s in self.sections: for i in range(s.n3d()): h.pt3dchange(i, s.x3d(i) + dx, s.y3d(i) + dy, s.z3d(i) + dz, s.diam3d(i), sec=s) @@ -392,8 +387,8 @@ def set_geometry(self): # Define 3D shape and position of cell. By default neuron uses xy plane # for height and xz plane for depth. This is opposite for model as a # whole, but convention is followed in this function ease use of gui. - sec_pts, sec_lens, sec_diams, _, topology = self.secs() - for sec in self.get_sections(): + sec_pts, sec_lens, sec_diams, _, topology = self.secs + for sec in self.sections: h.pt3dclear(sec=sec) sec_name = sec.name().split('_', 1)[1] for pt in sec_pts[sec_name]: diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index f3d45ca58..37cd9a7f0 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -7,7 +7,7 @@ from neuron import h -from .cell import _Cell +from .cell import Cell from .params import compare_dictionaries from .params_default import (get_L2Pyr_params_default, @@ -84,49 +84,6 @@ def _get_syn_props(p_all, cell_type): } -class BasketSingle(_Cell): - """Inhibitory cell class. - - Attributes - ---------- - synapses : dict - The synapses that the cell can use for connections. - sect_loc : dict of list - Can have keys 'proximal' or 'distal' each containing - names of section locations that are proximal or distal. - """ - - def __init__(self, pos, cell_name='Basket', gid=None): - self.props = _get_basket_soma_props(cell_name, pos) - _Cell.__init__(self, self.props, gid=gid) - # store cell name for later - self.name = cell_name - self.set_geometry() - self.synapses = dict() - self._synapse_create() - self.set_biophysics() - - def set_biophysics(self): - self.soma.insert('hh2') - - def secs(self): - return _secs_Basket() - - def get_sections(self): - """Get sections.""" - return [self.soma] - - # creation of synapses - def _synapse_create(self): - # creates synapses onto this cell - self.synapses['soma_ampa'] = self.syn_create( - self.soma(0.5), e=0., tau1=0.5, tau2=5.) - self.synapses['soma_gabaa'] = self.syn_create( - self.soma(0.5), e=-80, tau1=0.5, tau2=5.) - self.synapses['soma_nmda'] = self.syn_create( - self.soma(0.5), e=0., tau1=1., tau2=20.) - - def basket(pos, cell_name='L2Basket', gid=None): """Get layer 2 basket cells. @@ -144,7 +101,26 @@ def basket(pos, cell_name='L2Basket', gid=None): cell : instance of BasketSingle The basket cell. """ - cell = BasketSingle(pos, cell_name=cell_name, gid=gid) + props = _get_basket_soma_props(cell_name, pos) + cell = Cell(props, gid=gid) + + cell.sections = [cell.soma] # XXX: needed? + cell.name = cell_name + cell.secs = _secs_Basket() + + cell.set_geometry() + + cell.synapses = dict() + # cell._synapse_create() + cell.synapses['soma_ampa'] = cell.syn_create( + cell.soma(0.5), e=0., tau1=0.5, tau2=5.) + cell.synapses['soma_gabaa'] = cell.syn_create( + cell.soma(0.5), e=-80, tau1=0.5, tau2=5.) + cell.synapses['soma_nmda'] = cell.syn_create( + cell.soma(0.5), e=0., tau1=1., tau2=20.) + + cell.soma.insert('hh2') + if cell_name == 'L2Basket': cell.sect_loc = dict(proximal=['soma'], distal=['soma']) elif cell_name == 'L5Basket': @@ -152,7 +128,7 @@ def basket(pos, cell_name='L2Basket', gid=None): return cell -class Pyr(_Cell): +class Pyr(Cell): """Pyramidal neuron. Parameters @@ -198,7 +174,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.name = 'L2Pyr' soma_props = _get_soma_props(p_all, self.name, pos) - _Cell.__init__(self, soma_props, gid=gid) + Cell.__init__(self, soma_props, gid=gid) self.create_soma() level2_keys = ['L', 'diam', 'Ra', 'cm'] @@ -210,10 +186,16 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra self.create_dends(p_dend) # just creates the sections + self.sections = [self.soma] + list(self.dends.values()) self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] self.sect_loc['distal'] = ['apicaltuft'] + if celltype == 'L5_pyramidal': + self.secs = _secs_L5Pyr() + else: + self.secs = _secs_L2Pyr() + # sets geom properties; adjusted after translation from # hoc (2009 model) self.set_geometry(p_dend) @@ -238,7 +220,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.set_biophysics(p_all) # insert dipole - yscale = self.secs()[3] + yscale = self.secs[3] self.insert_dipole(yscale) # create synapses @@ -259,7 +241,7 @@ def set_geometry(self, p_dend): * cm: membrane capacitance in micro-Farads * Ra: axial resistivity in ohm-cm """ - _Cell.set_geometry(self) + Cell.set_geometry(self) # resets length,diam,etc. based on param specification for key in p_dend: # set dend props @@ -283,9 +265,6 @@ def create_dends(self, p_dend_props): self.dends[key] = h.Section( name=self.name + '_' + key) # create dend - def get_sections(self): - return [self.soma] + list(self.dends.values()) - def _synapse_create(self, p_syn): """Creates synapses onto this cell.""" # Somatic synapses @@ -329,9 +308,6 @@ def section_names(self): return ['apical_trunk', 'apical_1', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - def secs(self): - return _secs_L2Pyr() - class L5Pyr(Pyr): """Layer 5 Pyramidal class. @@ -362,12 +338,9 @@ def section_names(self): return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - def secs(self): - return _secs_L5Pyr() - def set_biophysics(self, p_all): "Set the biophysics for the default Pyramidal cell." - _Cell.set_biophysics(self, p_all) + Cell.set_biophysics(self, p_all) self.soma.insert('ar') self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index 4e43edbd2..c64d2d8ed 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -3,7 +3,7 @@ import matplotlib from hnn_core.network_builder import load_custom_mechanisms -from hnn_core.cell import _ArtificialCell, _Cell +from hnn_core.cell import _ArtificialCell, Cell matplotlib.use('agg') @@ -14,13 +14,6 @@ def test_cell(): soma_props = {"L": 22.1, "diam": 23.4, "cm": 0.6195, "Ra": 200.0, "pos": (0., 0., 0.), 'name': 'test_cell'} - with pytest.raises(TypeError, match='with abstract methods get_sections'): - cell = _Cell(soma_props=soma_props) - - class Cell(_Cell): - def get_sections(self): - return [self.soma] - # GID is assigned exactly once for each cell, either at initialisation... cell = Cell(soma_props=soma_props, gid=42) assert cell.gid == 42 From d3631ec8de89ea1ecd89cd1c7057e110599f14ed Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 21:54:11 -0400 Subject: [PATCH 16/49] Move set_biophysics to init --- hnn_core/cell_classes.py | 53 +++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 37cd9a7f0..dee9ca6ae 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -219,6 +219,31 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # biophysics self.set_biophysics(p_all) + if celltype == 'L5_pyramidal': + self.soma.insert('ar') + self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] + + # set dend biophysics not specified in Pyr() + for key in self.dends: + # insert 'ar' mechanism + self.dends[key].insert('ar') + + # set gbar_ar + # Value depends on distance from the soma. Soma is set as + # origin by passing self.soma as a sec argument to h.distance() + # Then iterate over segment nodes of dendritic sections + # and set gbar_ar depending on h.distance(seg.x), which returns + # distance from the soma to this point on the CURRENTLY ACCESSED + # SECTION!!! + h.distance(sec=self.soma) + + for key in self.dends: + self.dends[key].push() + for seg in self.dends[key]: + seg.gbar_ar = 1e-6 * np.exp(3e-3 * h.distance(seg.x)) + + h.pop_section() + # insert dipole yscale = self.secs[3] self.insert_dipole(yscale) @@ -337,31 +362,3 @@ def __init__(self, pos=None, override_params=None, gid=None): def section_names(self): return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - - def set_biophysics(self, p_all): - "Set the biophysics for the default Pyramidal cell." - Cell.set_biophysics(self, p_all) - - self.soma.insert('ar') - self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] - - # set dend biophysics not specified in Pyr() - for key in self.dends: - # insert 'ar' mechanism - self.dends[key].insert('ar') - - # set gbar_ar - # Value depends on distance from the soma. Soma is set as - # origin by passing self.soma as a sec argument to h.distance() - # Then iterate over segment nodes of dendritic sections - # and set gbar_ar depending on h.distance(seg.x), which returns - # distance from the soma to this point on the CURRENTLY ACCESSED - # SECTION!!! - h.distance(sec=self.soma) - - for key in self.dends: - self.dends[key].push() - for seg in self.dends[key]: - seg.gbar_ar = 1e-6 * np.exp(3e-3 * h.distance(seg.x)) - - h.pop_section() From 7a989f8fa606bd2a15e924a7f102fc062ab681a0 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 29 Apr 2021 23:22:37 -0400 Subject: [PATCH 17/49] No more L2Pyr and L5Pyr --- hnn_core/__init__.py | 2 +- hnn_core/cell_classes.py | 66 +++++-------------------------------- hnn_core/network_builder.py | 6 ++-- 3 files changed, 12 insertions(+), 62 deletions(-) diff --git a/hnn_core/__init__.py b/hnn_core/__init__.py index c010fffe7..7f5626e96 100644 --- a/hnn_core/__init__.py +++ b/hnn_core/__init__.py @@ -3,7 +3,7 @@ from .params import Params, read_params from .network import Network from .cell_response import CellResponse, read_spikes -from .cell_classes import L2Pyr, L5Pyr, basket +from .cell_classes import Pyr, basket from .parallel_backends import MPIBackend, JoblibBackend __version__ = '0.2.dev0' diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index dee9ca6ae..ac0815ca0 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -281,6 +281,14 @@ def set_geometry(self, p_dend): if not self.dends[key].nseg % 2: self.dends[key].nseg += 1 + def section_names(self): + if self.name == 'L2Pyr': + return ['apical_trunk', 'apical_1', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + else: + return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + def create_dends(self, p_dend_props): """Create dendrites.""" # XXX: name should be unique even across cell types? @@ -304,61 +312,3 @@ def _synapse_create(self, p_syn): syn_key = sec.replace('_', '') + '_' + receptor self.synapses[syn_key] = self.syn_create( self.dends[sec](0.5), **p_syn[receptor]) - - -class L2Pyr(Pyr): - """Layer 2 pyramidal cell class. - - Parameters - ---------- - pos : tuple - Coordinates of cell soma in xyz-space - override_params : dict or None (optional) - Parameters specific to L2 pyramidal neurons to override the default set - gid : int or None (optional) - Each cell in a network is uniquely identified by it's "global ID": GID. - The GID is an integer from 0 to n_cells, or None if the cell is not - yet attached to a network. Once the GID is set, it cannot be changed. - - Attributes - ---------- - name : str - The name of the cell - """ - - def __init__(self, pos=None, override_params=None, gid=None): - Pyr.__init__(self, pos, 'L2_pyramidal', override_params, gid=gid) - - def section_names(self): - return ['apical_trunk', 'apical_1', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - - -class L5Pyr(Pyr): - """Layer 5 Pyramidal class. - - Parameters - ---------- - pos : tuple - Coordinates of cell soma in xyz-space - override_params : dict or None (optional) - Parameters specific to L2 pyramidal neurons to override the default set - gid : int or None (optional) - Each cell in a network is uniquely identified by it's "global ID": GID. - The GID is an integer from 0 to n_cells, or None if the cell is not - yet attached to a network. Once the GID is set, it cannot be changed. - - Attributes - ---------- - name : str - The name of the cell - """ - - def __init__(self, pos=None, override_params=None, gid=None): - """Get default L5Pyr params and update them with - corresponding params in p.""" - Pyr.__init__(self, pos, 'L5_pyramidal', override_params, gid=gid) - - def section_names(self): - return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 7d20958f6..97764b9cc 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -13,7 +13,7 @@ h.nrnunit_use_legacy(1) from .cell import _ArtificialCell -from .cell_classes import L2Pyr, L5Pyr, basket +from .cell_classes import Pyr, basket from .params import _long_name, _short_name from copy import deepcopy @@ -376,7 +376,7 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, These drives are spike SOURCES but cells are also targets. External inputs are not targets. """ - type2class = {'L2_pyramidal': L2Pyr, 'L5_pyramidal': L5Pyr, + type2class = {'L2_pyramidal': Pyr, 'L5_pyramidal': Pyr, 'L2_basket': basket, 'L5_basket': basket} # loop through ALL gids # have to loop over self._gid_list, since this is what we got @@ -393,7 +393,7 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, # XXX Why doesn't a _Cell have a .threshold? Would make a # lot of sense to include it, as _ArtificialCells do. cell = PyramidalCell(src_pos, override_params=None, - gid=gid) + celltype=src_type, gid=gid) else: basket_cell = type2class[src_type] cell = basket_cell(src_pos, cell_name=_short_name(src_type), From 0efbbc1eaace49ded41368f2ebdd3fb95d5352b8 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 07:02:06 -0400 Subject: [PATCH 18/49] MAINT: more reorganization. Don't create soma on cell init --- hnn_core/cell.py | 1 - hnn_core/cell_classes.py | 45 +++++++++++++++++----------------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 1a2750238..7fef6074e 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -117,7 +117,6 @@ def __init__(self, soma_props, gid=None): self.dends = dict() self.synapses = dict() self.sect_loc = dict() - self.create_soma() self.rec_v = h.Vector() self.rec_i = dict() # insert iclamp diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index ac0815ca0..435f1ea3b 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -104,6 +104,7 @@ def basket(pos, cell_name='L2Basket', gid=None): props = _get_basket_soma_props(cell_name, pos) cell = Cell(props, gid=gid) + cell.create_soma() cell.sections = [cell.soma] # XXX: needed? cell.name = cell_name cell.secs = _secs_Basket() @@ -157,8 +158,25 @@ def __init__(self, pos, celltype, override_params=None, gid=None): if celltype == 'L5_pyramidal': p_all_default = get_L5Pyr_params_default() + self.name = 'L5Pyr' + self.secs = _secs_L5Pyr() + self.mechanisms = { + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2'], + 'ca': ['gbar_ca'], + 'cad': ['taur_cad'], + 'kca': ['gbar_kca'], + 'km': ['gbar_km'], + 'cat': ['gbar_cat'] + } elif celltype == 'L2_pyramidal': p_all_default = get_L2Pyr_params_default() + self.name = 'L2Pyr' + self.secs = _secs_L2Pyr() + self.mechanisms = { + 'km': ['gbar_km'], + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2']} else: raise ValueError(f'Unknown pyramidal cell type: {celltype}') @@ -168,14 +186,9 @@ def __init__(self, pos, celltype, override_params=None, gid=None): p_all = compare_dictionaries(p_all_default, override_params) # Get somatic, dendritic, and synapse properties - if celltype == 'L5_pyramidal': - self.name = 'L5Pyr' - else: - self.name = 'L2Pyr' soma_props = _get_soma_props(p_all, self.name, pos) Cell.__init__(self, soma_props, gid=gid) - self.create_soma() level2_keys = ['L', 'diam', 'Ra', 'cm'] p_dend = _flat_to_nested(p_all, cell_type=self.name, @@ -185,37 +198,17 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra + self.create_soma() self.create_dends(p_dend) # just creates the sections self.sections = [self.soma] + list(self.dends.values()) self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] self.sect_loc['distal'] = ['apicaltuft'] - if celltype == 'L5_pyramidal': - self.secs = _secs_L5Pyr() - else: - self.secs = _secs_L2Pyr() - # sets geom properties; adjusted after translation from # hoc (2009 model) self.set_geometry(p_dend) - if celltype == 'L5_pyramidal': - self.mechanisms = { - 'hh2': ['gkbar_hh2', 'gnabar_hh2', - 'gl_hh2', 'el_hh2'], - 'ca': ['gbar_ca'], - 'cad': ['taur_cad'], - 'kca': ['gbar_kca'], - 'km': ['gbar_km'], - 'cat': ['gbar_cat'] - } - else: - self.mechanisms = { - 'km': ['gbar_km'], - 'hh2': ['gkbar_hh2', 'gnabar_hh2', - 'gl_hh2', 'el_hh2']} - # biophysics self.set_biophysics(p_all) From cb09a5cd2491ae4bcaddbeacb36bf14f68cbdcd7 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 07:14:47 -0400 Subject: [PATCH 19/49] ENH: move pos out of soma_props --- hnn_core/cell.py | 13 ++++++------- hnn_core/cell_classes.py | 14 ++++++-------- hnn_core/tests/test_cell.py | 9 +++++---- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 7fef6074e..5574ab343 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -78,7 +78,9 @@ class Cell(ABC): ---------- soma_props : dict The properties of the soma. Must contain - keys 'L', 'diam', and 'pos' + keys 'L', 'diam'. + pos : tuple + The (x, y, z) coordinates. gid : int or None (optional) Each cell in a network is uniquely identified by it's "global ID": GID. The GID is an integer from 0 to n_cells, or None if the cell is not @@ -109,9 +111,8 @@ class Cell(ABC): GID of the cell in a network (or None if not yet assigned) """ - def __init__(self, soma_props, gid=None): - # variable for the list_IClamp - self.list_IClamp = None + def __init__(self, soma_props, pos, gid=None): + self.pos = pos self.soma_props = soma_props # preallocate dict to store dends self.dends = dict() @@ -120,7 +121,7 @@ def __init__(self, soma_props, gid=None): self.rec_v = h.Vector() self.rec_i = dict() # insert iclamp - self.list_IClamp = [] + self.list_IClamp = list() self._gid = None self.tonic_biases = list() if gid is not None: @@ -170,8 +171,6 @@ def create_soma(self): # Used in shape_change() b/c func clobbers self.soma.L, self.soma.diam soma_props = self.soma_props - self.pos = soma_props['pos'] - self.soma = h.Section(cell=self, name=soma_props['name'] + '_soma') self.soma.L = soma_props['L'] self.soma.diam = soma_props['diam'] diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 435f1ea3b..3eca17125 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -36,10 +36,9 @@ def _flat_to_nested(params, cell_type, level1_keys, level2_keys): return nested_dict -def _get_soma_props(p_all, cell_type, pos): +def _get_soma_props(p_all, cell_type): """Hardcoded somatic properties.""" return { - 'pos': pos, 'L': p_all[f'{cell_type}_soma_L'], 'diam': p_all[f'{cell_type}_soma_diam'], 'cm': p_all[f'{cell_type}_soma_cm'], @@ -48,9 +47,8 @@ def _get_soma_props(p_all, cell_type, pos): } -def _get_basket_soma_props(cell_name, pos): +def _get_basket_soma_props(cell_name): return { - 'pos': pos, 'L': 39., 'diam': 20., 'cm': 0.85, @@ -101,8 +99,8 @@ def basket(pos, cell_name='L2Basket', gid=None): cell : instance of BasketSingle The basket cell. """ - props = _get_basket_soma_props(cell_name, pos) - cell = Cell(props, gid=gid) + props = _get_basket_soma_props(cell_name) + cell = Cell(props, pos=pos, gid=gid) cell.create_soma() cell.sections = [cell.soma] # XXX: needed? @@ -186,9 +184,9 @@ def __init__(self, pos, celltype, override_params=None, gid=None): p_all = compare_dictionaries(p_all_default, override_params) # Get somatic, dendritic, and synapse properties - soma_props = _get_soma_props(p_all, self.name, pos) + soma_props = _get_soma_props(p_all, self.name) - Cell.__init__(self, soma_props, gid=gid) + Cell.__init__(self, soma_props, pos=pos, gid=gid) level2_keys = ['L', 'diam', 'Ra', 'cm'] p_dend = _flat_to_nested(p_all, cell_type=self.name, diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index c64d2d8ed..13275e800 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -12,16 +12,17 @@ def test_cell(): """Test cells object.""" # test that ExpSyn always takes nrn.Segment, not float soma_props = {"L": 22.1, "diam": 23.4, "cm": 0.6195, "Ra": 200.0, - "pos": (0., 0., 0.), 'name': 'test_cell'} + 'name': 'test_cell'} + pos = (0., 0., 0.) # GID is assigned exactly once for each cell, either at initialisation... - cell = Cell(soma_props=soma_props, gid=42) + cell = Cell(soma_props=soma_props, pos=pos, gid=42) assert cell.gid == 42 with pytest.raises(RuntimeError, match='Global ID for this cell already assigned!'): cell.gid += 1 # ... or later - cell = Cell(soma_props=soma_props) # cells can exist fine without gid + cell = Cell(soma_props=soma_props, pos=pos) # cells can exist fine without gid assert cell.gid is None # check that it's initialised to None with pytest.raises(ValueError, match='gid must be an integer'): @@ -30,7 +31,7 @@ def test_cell(): assert cell.gid == 42 with pytest.raises(ValueError, match='gid must be an integer'): - cell = Cell(soma_props=soma_props, gid='one') # test init checks gid + cell = Cell(soma_props=soma_props, pos=pos, gid='one') # test init checks gid with pytest.raises(TypeError, match='secloc must be instance of'): cell.syn_create(0.5, e=0., tau1=0.5, tau2=5.) From 3839f1e7f80bbf7e57c7989e37453e3b9473128b Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 08:09:58 -0400 Subject: [PATCH 20/49] ENH: move create_dends to parent class --- hnn_core/cell.py | 17 ++++++++++++++--- hnn_core/cell_classes.py | 14 -------------- hnn_core/network_builder.py | 3 ++- hnn_core/tests/test_cell.py | 6 ++++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 5574ab343..4e0d590e0 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -3,8 +3,6 @@ # Authors: Mainak Jas # Sam Neymotin -from abc import ABC, abstractmethod - import numpy as np from neuron import h, nrn @@ -71,7 +69,7 @@ def gid(self, gid): raise RuntimeError('Global ID for this cell already assigned!') -class Cell(ABC): +class Cell: """Create a cell object. Parameters @@ -165,6 +163,19 @@ def set_biophysics(self, p_all): for attr in attrs: setattr(sec, attr, p_all[f'{self.name}_{sec_name}_{attr}']) + def create_dends(self, p_dend): + """Create dendrites.""" + # XXX: name should be unique even across cell types? + # otherwise Neuron cannot disambiguate, hence + # self.name + '_' + key + for key in p_dend: + self.dends[key] = h.Section( + name=self.name + '_' + key) # create dend + self.dends[key].L = p_dend[key]['L'] + self.dends[key].diam = p_dend[key]['diam'] + self.dends[key].Ra = p_dend[key]['Ra'] + self.dends[key].cm = p_dend[key]['cm'] + def create_soma(self): """Create soma and set geometry.""" # make L_soma and diam_soma elements of self diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 3eca17125..e3abd2716 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -260,11 +260,6 @@ def set_geometry(self, p_dend): Cell.set_geometry(self) # resets length,diam,etc. based on param specification for key in p_dend: - # set dend props - self.dends[key].L = p_dend[key]['L'] - self.dends[key].diam = p_dend[key]['diam'] - self.dends[key].Ra = p_dend[key]['Ra'] - self.dends[key].cm = p_dend[key]['cm'] # set dend nseg if p_dend[key]['L'] > 100.: self.dends[key].nseg = int(p_dend[key]['L'] / 50.) @@ -280,15 +275,6 @@ def section_names(self): return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - def create_dends(self, p_dend_props): - """Create dendrites.""" - # XXX: name should be unique even across cell types? - # otherwise Neuron cannot disambiguate, hence - # self.name + '_' + key - for key in p_dend_props: - self.dends[key] = h.Section( - name=self.name + '_' + key) # create dend - def _synapse_create(self, p_syn): """Creates synapses onto this cell.""" # Somatic synapses diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 97764b9cc..736fe9da7 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -396,7 +396,8 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, celltype=src_type, gid=gid) else: basket_cell = type2class[src_type] - cell = basket_cell(src_pos, cell_name=_short_name(src_type), + cell = basket_cell(src_pos, + cell_name=_short_name(src_type), gid=gid) if ('tonic' in self.net.external_biases and src_type in self.net.external_biases['tonic']): diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index 13275e800..f949b542a 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -22,7 +22,8 @@ def test_cell(): match='Global ID for this cell already assigned!'): cell.gid += 1 # ... or later - cell = Cell(soma_props=soma_props, pos=pos) # cells can exist fine without gid + # cells can exist fine without gid + cell = Cell(soma_props=soma_props, pos=pos) assert cell.gid is None # check that it's initialised to None with pytest.raises(ValueError, match='gid must be an integer'): @@ -31,7 +32,8 @@ def test_cell(): assert cell.gid == 42 with pytest.raises(ValueError, match='gid must be an integer'): - cell = Cell(soma_props=soma_props, pos=pos, gid='one') # test init checks gid + # test init checks gid + cell = Cell(soma_props=soma_props, pos=pos, gid='one') with pytest.raises(TypeError, match='secloc must be instance of'): cell.syn_create(0.5, e=0., tau1=0.5, tau2=5.) From 3b951eb21e2a0c955c6dbc9838afc6860d0f3d5c Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 08:27:07 -0400 Subject: [PATCH 21/49] ENH: cell shouldn't take soma_props as argument --- hnn_core/cell.py | 27 +++++++++++---------------- hnn_core/cell_classes.py | 18 ++++++++---------- hnn_core/tests/test_cell.py | 10 ++++------ 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 4e0d590e0..01c3e9443 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -74,9 +74,6 @@ class Cell: Parameters ---------- - soma_props : dict - The properties of the soma. Must contain - keys 'L', 'diam'. pos : tuple The (x, y, z) coordinates. gid : int or None (optional) @@ -109,9 +106,8 @@ class Cell: GID of the cell in a network (or None if not yet assigned) """ - def __init__(self, soma_props, pos, gid=None): + def __init__(self, pos, gid=None): self.pos = pos - self.soma_props = soma_props # preallocate dict to store dends self.dends = dict() self.synapses = dict() @@ -127,11 +123,7 @@ def __init__(self, soma_props, pos, gid=None): def __repr__(self): class_name = self.__class__.__name__ - soma_props = self.soma_props - s = ('soma: L %f, diam %f, Ra %f, cm %f' % - (soma_props['L'], soma_props['diam'], - soma_props['Ra'], soma_props['cm'])) - return '<%s | %s>' % (class_name, s) + return f'<{class_name} | gid={self._gid}>' @property def gid(self): @@ -176,13 +168,16 @@ def create_dends(self, p_dend): self.dends[key].Ra = p_dend[key]['Ra'] self.dends[key].cm = p_dend[key]['cm'] - def create_soma(self): - """Create soma and set geometry.""" - # make L_soma and diam_soma elements of self - # Used in shape_change() b/c func clobbers self.soma.L, self.soma.diam - soma_props = self.soma_props + def create_soma(self, soma_props): + """Create soma and set geometry. - self.soma = h.Section(cell=self, name=soma_props['name'] + '_soma') + Parameters + ---------- + soma_props : dict + The properties of the soma. Must contain + keys 'L', 'diam', 'Ra', and 'cm'. + """ + self.soma = h.Section(cell=self, name=self.name + '_soma') self.soma.L = soma_props['L'] self.soma.diam = soma_props['diam'] self.soma.Ra = soma_props['Ra'] diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index e3abd2716..cfea2a21c 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -42,8 +42,7 @@ def _get_soma_props(p_all, cell_type): 'L': p_all[f'{cell_type}_soma_L'], 'diam': p_all[f'{cell_type}_soma_diam'], 'cm': p_all[f'{cell_type}_soma_cm'], - 'Ra': p_all[f'{cell_type}_soma_Ra'], - 'name': cell_type, + 'Ra': p_all[f'{cell_type}_soma_Ra'] } @@ -52,8 +51,7 @@ def _get_basket_soma_props(cell_name): 'L': 39., 'diam': 20., 'cm': 0.85, - 'Ra': 200., - 'name': cell_name, + 'Ra': 200. } @@ -99,12 +97,12 @@ def basket(pos, cell_name='L2Basket', gid=None): cell : instance of BasketSingle The basket cell. """ - props = _get_basket_soma_props(cell_name) - cell = Cell(props, pos=pos, gid=gid) + cell = Cell(pos=pos, gid=gid) + cell.name = cell_name - cell.create_soma() + soma_props = _get_basket_soma_props(cell_name) + cell.create_soma(soma_props) cell.sections = [cell.soma] # XXX: needed? - cell.name = cell_name cell.secs = _secs_Basket() cell.set_geometry() @@ -186,7 +184,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # Get somatic, dendritic, and synapse properties soma_props = _get_soma_props(p_all, self.name) - Cell.__init__(self, soma_props, pos=pos, gid=gid) + Cell.__init__(self, pos=pos, gid=gid) level2_keys = ['L', 'diam', 'Ra', 'cm'] p_dend = _flat_to_nested(p_all, cell_type=self.name, @@ -196,7 +194,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra - self.create_soma() + self.create_soma(soma_props) self.create_dends(p_dend) # just creates the sections self.sections = [self.soma] + list(self.dends.values()) diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index f949b542a..23172d763 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -10,20 +10,17 @@ def test_cell(): """Test cells object.""" - # test that ExpSyn always takes nrn.Segment, not float - soma_props = {"L": 22.1, "diam": 23.4, "cm": 0.6195, "Ra": 200.0, - 'name': 'test_cell'} pos = (0., 0., 0.) # GID is assigned exactly once for each cell, either at initialisation... - cell = Cell(soma_props=soma_props, pos=pos, gid=42) + cell = Cell(pos=pos, gid=42) assert cell.gid == 42 with pytest.raises(RuntimeError, match='Global ID for this cell already assigned!'): cell.gid += 1 # ... or later # cells can exist fine without gid - cell = Cell(soma_props=soma_props, pos=pos) + cell = Cell(pos=pos) assert cell.gid is None # check that it's initialised to None with pytest.raises(ValueError, match='gid must be an integer'): @@ -33,8 +30,9 @@ def test_cell(): with pytest.raises(ValueError, match='gid must be an integer'): # test init checks gid - cell = Cell(soma_props=soma_props, pos=pos, gid='one') + cell = Cell(pos=pos, gid='one') + # test that ExpSyn always takes nrn.Segment, not float with pytest.raises(TypeError, match='secloc must be instance of'): cell.syn_create(0.5, e=0., tau1=0.5, tau2=5.) From 759f22a07ac33e683bc36339a3e0ce74ad442031 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 17:53:06 -0400 Subject: [PATCH 22/49] MAINT: remove another method --- hnn_core/cell_classes.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index cfea2a21c..b1f90dc14 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -165,6 +165,9 @@ def __init__(self, pos, celltype, override_params=None, gid=None): 'km': ['gbar_km'], 'cat': ['gbar_cat'] } + section_names = ['apical_trunk', 'apical_1', + 'apical_2', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] elif celltype == 'L2_pyramidal': p_all_default = get_L2Pyr_params_default() self.name = 'L2Pyr' @@ -173,6 +176,8 @@ def __init__(self, pos, celltype, override_params=None, gid=None): 'km': ['gbar_km'], 'hh2': ['gkbar_hh2', 'gnabar_hh2', 'gl_hh2', 'el_hh2']} + section_names = ['apical_trunk', 'apical_1', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] else: raise ValueError(f'Unknown pyramidal cell type: {celltype}') @@ -181,20 +186,19 @@ def __init__(self, pos, celltype, override_params=None, gid=None): assert isinstance(override_params, dict) p_all = compare_dictionaries(p_all_default, override_params) - # Get somatic, dendritic, and synapse properties - soma_props = _get_soma_props(p_all, self.name) - Cell.__init__(self, pos=pos, gid=gid) level2_keys = ['L', 'diam', 'Ra', 'cm'] + # Get somatic, dendritic, and synapse properties + p_soma = _get_soma_props(p_all, self.name) p_dend = _flat_to_nested(p_all, cell_type=self.name, - level1_keys=self.section_names(), + level1_keys=section_names, level2_keys=level2_keys) p_syn = _get_syn_props(p_all, self.name) # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra - self.create_soma(soma_props) + self.create_soma(p_soma) self.create_dends(p_dend) # just creates the sections self.sections = [self.soma] + list(self.dends.values()) @@ -238,7 +242,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.insert_dipole(yscale) # create synapses - self._synapse_create(p_syn) + self._synapse_create(p_syn, section_names) def set_geometry(self, p_dend): """Define shape of the neuron and connect sections. @@ -265,15 +269,7 @@ def set_geometry(self, p_dend): if not self.dends[key].nseg % 2: self.dends[key].nseg += 1 - def section_names(self): - if self.name == 'L2Pyr': - return ['apical_trunk', 'apical_1', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - else: - return ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - - def _synapse_create(self, p_syn): + def _synapse_create(self, p_syn, section_names): """Creates synapses onto this cell.""" # Somatic synapses self.synapses['soma_gabaa'] = self.syn_create(self.soma(0.5), @@ -282,7 +278,7 @@ def _synapse_create(self, p_syn): **p_syn['gabab']) # Dendritic synapses - for sec in self.section_names(): + for sec in section_names: for receptor in p_syn: syn_key = sec.replace('_', '') + '_' + receptor self.synapses[syn_key] = self.syn_create( From 58dbe27175c8df34dfa85c3dbd6a70436c810bce Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 22:15:00 -0400 Subject: [PATCH 23/49] Mechanism by section --- hnn_core/cell.py | 12 +++++------ hnn_core/cell_classes.py | 44 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 01c3e9443..3fc6eb7c3 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -138,7 +138,7 @@ def gid(self, gid): else: raise RuntimeError('Global ID for this cell already assigned!') - def set_biophysics(self, p_all): + def set_biophysics(self, p_mech): "Set the biophysics for the default Pyramidal cell." # neuron syntax is used to set values for mechanisms @@ -146,14 +146,12 @@ def set_biophysics(self, p_all): # in a section. This method is significantly faster than using # a for loop to iterate over all segments to set mech values - # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] for sec in self.sections: sec_name = sec.name().split('_', 1)[1] - sec_name = 'soma' if sec_name == 'soma' else 'dend' - for key, attrs in self.mechanisms.items(): - sec.insert(key) - for attr in attrs: - setattr(sec, attr, p_all[f'{self.name}_{sec_name}_{attr}']) + for mech_name in p_mech[sec_name]: + sec.insert(mech_name) + for attr in p_mech[sec_name][mech_name]: + setattr(sec, attr, p_mech[sec_name][mech_name][attr]) def create_dends(self, p_dend): """Create dendrites.""" diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index b1f90dc14..e03887ade 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -80,6 +80,41 @@ def _get_syn_props(p_all, cell_type): } +def _get_mechanisms(p_all, cell_type, section_names, mechanisms): + """Get mechanism + + Parameters + ---------- + cell_type : str + The cell type + section_names : str + The section_names + mechanisms : dict of list + The mechanism properties to extract + + Returns + ------- + mech_props : dict of dict of dict + Nested dictionary of the form + sections -> mechanism -> mechanism properties + used to instantiate the mechanism in Neuron + """ + mech_props = dict() + for sec_name in section_names: + this_sec_prop = dict() + for mech_name in mechanisms: + this_mech_prop = dict() + for mech_attr in mechanisms[mech_name]: + if sec_name == 'soma': + key = f'{cell_type}_soma_{mech_attr}' + else: + key = f'{cell_type}_dend_{mech_attr}' + this_mech_prop[mech_attr] = p_all[key] + this_sec_prop[mech_name] = this_mech_prop + mech_props[sec_name] = this_sec_prop + return mech_props + + def basket(pos, cell_name='L2Basket', gid=None): """Get layer 2 basket cells. @@ -156,7 +191,8 @@ def __init__(self, pos, celltype, override_params=None, gid=None): p_all_default = get_L5Pyr_params_default() self.name = 'L5Pyr' self.secs = _secs_L5Pyr() - self.mechanisms = { + # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] + mechanisms = { 'hh2': ['gkbar_hh2', 'gnabar_hh2', 'gl_hh2', 'el_hh2'], 'ca': ['gbar_ca'], @@ -172,7 +208,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): p_all_default = get_L2Pyr_params_default() self.name = 'L2Pyr' self.secs = _secs_L2Pyr() - self.mechanisms = { + mechanisms = { 'km': ['gbar_km'], 'hh2': ['gkbar_hh2', 'gnabar_hh2', 'gl_hh2', 'el_hh2']} @@ -195,6 +231,8 @@ def __init__(self, pos, celltype, override_params=None, gid=None): level1_keys=section_names, level2_keys=level2_keys) p_syn = _get_syn_props(p_all, self.name) + p_mech = _get_mechanisms(p_all, self.name, ['soma'] + section_names, + mechanisms) # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra @@ -210,7 +248,7 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.set_geometry(p_dend) # biophysics - self.set_biophysics(p_all) + self.set_biophysics(p_mech) if celltype == 'L5_pyramidal': self.soma.insert('ar') From 6f3f7d4ce9e444d46c6b3f7dc1171b714f84a99e Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 22:22:58 -0400 Subject: [PATCH 24/49] Better name --- hnn_core/cell_classes.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index e03887ade..8e5e52a40 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -20,20 +20,20 @@ # units for taur: ms -def _flat_to_nested(params, cell_type, level1_keys, level2_keys): +def _get_dend_props(params, cell_type, section_names, prop_names): """Convert a flat dictionary to a nested dictionary.""" - nested_dict = dict() - for level1_key in level1_keys: - level2_dict = dict() - for key in level2_keys: + dend_props = dict() + for section_name in section_names: + dend_prop = dict() + for key in prop_names: if key in ['Ra', 'cm']: middle = 'dend' else: # map apicaltrunk -> apical_trunk etc. - middle = level1_key.replace('_', '') - level2_dict[key] = params[f'{cell_type}_{middle}_{key}'] - nested_dict[level1_key] = level2_dict - return nested_dict + middle = section_name.replace('_', '') + dend_prop[key] = params[f'{cell_type}_{middle}_{key}'] + dend_props[section_name] = dend_prop + return dend_props def _get_soma_props(p_all, cell_type): @@ -224,12 +224,12 @@ def __init__(self, pos, celltype, override_params=None, gid=None): Cell.__init__(self, pos=pos, gid=gid) - level2_keys = ['L', 'diam', 'Ra', 'cm'] + prop_names = ['L', 'diam', 'Ra', 'cm'] # Get somatic, dendritic, and synapse properties p_soma = _get_soma_props(p_all, self.name) - p_dend = _flat_to_nested(p_all, cell_type=self.name, - level1_keys=section_names, - level2_keys=level2_keys) + p_dend = _get_dend_props(p_all, cell_type=self.name, + section_names=section_names, + prop_names=prop_names) p_syn = _get_syn_props(p_all, self.name) p_mech = _get_mechanisms(p_all, self.name, ['soma'] + section_names, mechanisms) From a8fa99b45d1330290e981d93f282d02a57ec17c8 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 22:42:20 -0400 Subject: [PATCH 25/49] MAINT: get rid of set_geometry too --- hnn_core/cell_classes.py | 50 ++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 8e5e52a40..60ad27ec4 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -21,7 +21,20 @@ def _get_dend_props(params, cell_type, section_names, prop_names): - """Convert a flat dictionary to a nested dictionary.""" + """Convert a flat dictionary to a nested dictionary. + + Returns + ------- + dend_props : dict + Nested dictionary. The outer dictionary has keys + with names of dendrites and the inner dictionary + specifies the geometry of these sections. + + * L: length of a section in microns + * diam: diameter of a section in microns + * cm: membrane capacitance in micro-Farads + * Ra: axial resistivity in ohm-cm + """ dend_props = dict() for section_name in section_names: dend_prop = dict() @@ -245,7 +258,15 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # sets geom properties; adjusted after translation from # hoc (2009 model) - self.set_geometry(p_dend) + self.set_geometry() + # resets length,diam,etc. based on param specification + for key in p_dend: + # set dend nseg + if p_dend[key]['L'] > 100.: + self.dends[key].nseg = int(p_dend[key]['L'] / 50.) + # make dend.nseg odd for all sections + if not self.dends[key].nseg % 2: + self.dends[key].nseg += 1 # biophysics self.set_biophysics(p_mech) @@ -282,31 +303,6 @@ def __init__(self, pos, celltype, override_params=None, gid=None): # create synapses self._synapse_create(p_syn, section_names) - def set_geometry(self, p_dend): - """Define shape of the neuron and connect sections. - - Parameters - ---------- - p_dend : dict | None - Nested dictionary. The outer dictionary has keys - with names of dendrites and the inner dictionary - specifies the geometry of these sections. - - * L: length of a section in microns - * diam: diameter of a section in microns - * cm: membrane capacitance in micro-Farads - * Ra: axial resistivity in ohm-cm - """ - Cell.set_geometry(self) - # resets length,diam,etc. based on param specification - for key in p_dend: - # set dend nseg - if p_dend[key]['L'] > 100.: - self.dends[key].nseg = int(p_dend[key]['L'] / 50.) - # make dend.nseg odd for all sections - if not self.dends[key].nseg % 2: - self.dends[key].nseg += 1 - def _synapse_create(self, p_syn, section_names): """Creates synapses onto this cell.""" # Somatic synapses From bf218161177228111396001cf89a5acd0bdfbec4 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 22:57:19 -0400 Subject: [PATCH 26/49] Last method gone --- hnn_core/cell_classes.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 60ad27ec4..09781a102 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -200,6 +200,7 @@ class Pyr(Cell): def __init__(self, pos, celltype, override_params=None, gid=None): + Cell.__init__(self, pos=pos, gid=gid) if celltype == 'L5_pyramidal': p_all_default = get_L5Pyr_params_default() self.name = 'L5Pyr' @@ -235,8 +236,6 @@ def __init__(self, pos, celltype, override_params=None, gid=None): assert isinstance(override_params, dict) p_all = compare_dictionaries(p_all_default, override_params) - Cell.__init__(self, pos=pos, gid=gid) - prop_names = ['L', 'diam', 'Ra', 'cm'] # Get somatic, dendritic, and synapse properties p_soma = _get_soma_props(p_all, self.name) @@ -301,11 +300,6 @@ def __init__(self, pos, celltype, override_params=None, gid=None): self.insert_dipole(yscale) # create synapses - self._synapse_create(p_syn, section_names) - - def _synapse_create(self, p_syn, section_names): - """Creates synapses onto this cell.""" - # Somatic synapses self.synapses['soma_gabaa'] = self.syn_create(self.soma(0.5), **p_syn['gabaa']) self.synapses['soma_gabab'] = self.syn_create(self.soma(0.5), From ce242ddf2d30fd03cda8894b76ed124371eb70c8 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 23:11:11 -0400 Subject: [PATCH 27/49] Pyr becomes function --- hnn_core/__init__.py | 2 +- hnn_core/cell.py | 3 + hnn_core/cell_classes.py | 239 +++++++++++++++++------------------- hnn_core/network_builder.py | 10 +- 4 files changed, 123 insertions(+), 131 deletions(-) diff --git a/hnn_core/__init__.py b/hnn_core/__init__.py index 7f5626e96..35d1222ff 100644 --- a/hnn_core/__init__.py +++ b/hnn_core/__init__.py @@ -3,7 +3,7 @@ from .params import Params, read_params from .network import Network from .cell_response import CellResponse, read_spikes -from .cell_classes import Pyr, basket +from .cell_classes import pyramidal, basket from .parallel_backends import MPIBackend, JoblibBackend __version__ = '0.2.dev0' diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 3fc6eb7c3..2bcd9889b 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -104,6 +104,9 @@ class Cell: for tonic biasing inputs. gid : int GID of the cell in a network (or None if not yet assigned) + sect_loc : dict of list + Can have keys 'proximal' or 'distal' each containing + names of section locations that are proximal or distal. """ def __init__(self, pos, gid=None): diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 09781a102..908adac34 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -173,7 +173,7 @@ def basket(pos, cell_name='L2Basket', gid=None): return cell -class Pyr(Cell): +def pyramidal(pos, celltype, override_params=None, gid=None): """Pyramidal neuron. Parameters @@ -185,129 +185,118 @@ class Pyr(Cell): gid : int or None (optional) Each cell in a network is uniquely identified by it's "global ID": GID. The GID is an integer from 0 to n_cells, or None if the cell is not - yet attached to a network. Once the GID is set, it cannot be changed.. - - Attributes - ---------- - name : str - The name of the cell, 'L5Pyr' or 'L2Pyr' - sect_loc : dict of list - Can have keys 'proximal' or 'distal' each containing - names of section locations that are proximal or distal. - synapses : dict - The synapses that the cell can use for connections. + yet attached to a network. Once the GID is set, it cannot be changed. """ - def __init__(self, pos, celltype, override_params=None, gid=None): - - Cell.__init__(self, pos=pos, gid=gid) - if celltype == 'L5_pyramidal': - p_all_default = get_L5Pyr_params_default() - self.name = 'L5Pyr' - self.secs = _secs_L5Pyr() - # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] - mechanisms = { - 'hh2': ['gkbar_hh2', 'gnabar_hh2', - 'gl_hh2', 'el_hh2'], - 'ca': ['gbar_ca'], - 'cad': ['taur_cad'], - 'kca': ['gbar_kca'], - 'km': ['gbar_km'], - 'cat': ['gbar_cat'] - } - section_names = ['apical_trunk', 'apical_1', - 'apical_2', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - elif celltype == 'L2_pyramidal': - p_all_default = get_L2Pyr_params_default() - self.name = 'L2Pyr' - self.secs = _secs_L2Pyr() - mechanisms = { - 'km': ['gbar_km'], - 'hh2': ['gkbar_hh2', 'gnabar_hh2', - 'gl_hh2', 'el_hh2']} - section_names = ['apical_trunk', 'apical_1', 'apical_tuft', - 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - else: - raise ValueError(f'Unknown pyramidal cell type: {celltype}') - - p_all = p_all_default - if override_params is not None: - assert isinstance(override_params, dict) - p_all = compare_dictionaries(p_all_default, override_params) - - prop_names = ['L', 'diam', 'Ra', 'cm'] - # Get somatic, dendritic, and synapse properties - p_soma = _get_soma_props(p_all, self.name) - p_dend = _get_dend_props(p_all, cell_type=self.name, - section_names=section_names, - prop_names=prop_names) - p_syn = _get_syn_props(p_all, self.name) - p_mech = _get_mechanisms(p_all, self.name, ['soma'] + section_names, - mechanisms) - - # Geometry - # dend Cm and dend Ra set using soma Cm and soma Ra - self.create_soma(p_soma) - self.create_dends(p_dend) # just creates the sections - self.sections = [self.soma] + list(self.dends.values()) - - self.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] - self.sect_loc['distal'] = ['apicaltuft'] - - # sets geom properties; adjusted after translation from - # hoc (2009 model) - self.set_geometry() - # resets length,diam,etc. based on param specification - for key in p_dend: - # set dend nseg - if p_dend[key]['L'] > 100.: - self.dends[key].nseg = int(p_dend[key]['L'] / 50.) - # make dend.nseg odd for all sections - if not self.dends[key].nseg % 2: - self.dends[key].nseg += 1 - - # biophysics - self.set_biophysics(p_mech) - - if celltype == 'L5_pyramidal': - self.soma.insert('ar') - self.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] - - # set dend biophysics not specified in Pyr() - for key in self.dends: - # insert 'ar' mechanism - self.dends[key].insert('ar') - - # set gbar_ar - # Value depends on distance from the soma. Soma is set as - # origin by passing self.soma as a sec argument to h.distance() - # Then iterate over segment nodes of dendritic sections - # and set gbar_ar depending on h.distance(seg.x), which returns - # distance from the soma to this point on the CURRENTLY ACCESSED - # SECTION!!! - h.distance(sec=self.soma) - - for key in self.dends: - self.dends[key].push() - for seg in self.dends[key]: - seg.gbar_ar = 1e-6 * np.exp(3e-3 * h.distance(seg.x)) - - h.pop_section() - - # insert dipole - yscale = self.secs[3] - self.insert_dipole(yscale) - - # create synapses - self.synapses['soma_gabaa'] = self.syn_create(self.soma(0.5), - **p_syn['gabaa']) - self.synapses['soma_gabab'] = self.syn_create(self.soma(0.5), - **p_syn['gabab']) - - # Dendritic synapses - for sec in section_names: - for receptor in p_syn: - syn_key = sec.replace('_', '') + '_' + receptor - self.synapses[syn_key] = self.syn_create( - self.dends[sec](0.5), **p_syn[receptor]) + cell = Cell(pos=pos, gid=gid) + if celltype == 'L5_pyramidal': + p_all_default = get_L5Pyr_params_default() + cell.name = 'L5Pyr' + cell.secs = _secs_L5Pyr() + # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] + mechanisms = { + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2'], + 'ca': ['gbar_ca'], + 'cad': ['taur_cad'], + 'kca': ['gbar_kca'], + 'km': ['gbar_km'], + 'cat': ['gbar_cat'] + } + section_names = ['apical_trunk', 'apical_1', + 'apical_2', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + elif celltype == 'L2_pyramidal': + p_all_default = get_L2Pyr_params_default() + cell.name = 'L2Pyr' + cell.secs = _secs_L2Pyr() + mechanisms = { + 'km': ['gbar_km'], + 'hh2': ['gkbar_hh2', 'gnabar_hh2', + 'gl_hh2', 'el_hh2']} + section_names = ['apical_trunk', 'apical_1', 'apical_tuft', + 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + else: + raise ValueError(f'Unknown pyramidal cell type: {celltype}') + + p_all = p_all_default + if override_params is not None: + assert isinstance(override_params, dict) + p_all = compare_dictionaries(p_all_default, override_params) + + prop_names = ['L', 'diam', 'Ra', 'cm'] + # Get somatic, dendritic, and synapse properties + p_soma = _get_soma_props(p_all, cell.name) + p_dend = _get_dend_props(p_all, cell_type=cell.name, + section_names=section_names, + prop_names=prop_names) + p_syn = _get_syn_props(p_all, cell.name) + p_mech = _get_mechanisms(p_all, cell.name, ['soma'] + section_names, + mechanisms) + + # Geometry + # dend Cm and dend Ra set using soma Cm and soma Ra + cell.create_soma(p_soma) + cell.create_dends(p_dend) # just creates the sections + cell.sections = [cell.soma] + list(cell.dends.values()) + + cell.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] + cell.sect_loc['distal'] = ['apicaltuft'] + + # sets geom properties; adjusted after translation from + # hoc (2009 model) + cell.set_geometry() + # resets length,diam,etc. based on param specification + for key in p_dend: + # set dend nseg + if p_dend[key]['L'] > 100.: + cell.dends[key].nseg = int(p_dend[key]['L'] / 50.) + # make dend.nseg odd for all sections + if not cell.dends[key].nseg % 2: + cell.dends[key].nseg += 1 + + # biophysics + cell.set_biophysics(p_mech) + + if celltype == 'L5_pyramidal': + cell.soma.insert('ar') + cell.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] + + # set dend biophysics not specified in Pyr() + for key in cell.dends: + # insert 'ar' mechanism + cell.dends[key].insert('ar') + + # set gbar_ar + # Value depends on distance from the soma. Soma is set as + # origin by passing cell.soma as a sec argument to h.distance() + # Then iterate over segment nodes of dendritic sections + # and set gbar_ar depending on h.distance(seg.x), which returns + # distance from the soma to this point on the CURRENTLY ACCESSED + # SECTION!!! + h.distance(sec=cell.soma) + + for key in cell.dends: + cell.dends[key].push() + for seg in cell.dends[key]: + seg.gbar_ar = 1e-6 * np.exp(3e-3 * h.distance(seg.x)) + + h.pop_section() + + # insert dipole + yscale = cell.secs[3] + cell.insert_dipole(yscale) + + # create synapses + cell.synapses['soma_gabaa'] = cell.syn_create(cell.soma(0.5), + **p_syn['gabaa']) + cell.synapses['soma_gabab'] = cell.syn_create(cell.soma(0.5), + **p_syn['gabab']) + + # Dendritic synapses + for sec in section_names: + for receptor in p_syn: + syn_key = sec.replace('_', '') + '_' + receptor + cell.synapses[syn_key] = cell.syn_create( + cell.dends[sec](0.5), **p_syn[receptor]) + return cell diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 736fe9da7..f01a081e6 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -13,7 +13,7 @@ h.nrnunit_use_legacy(1) from .cell import _ArtificialCell -from .cell_classes import Pyr, basket +from .cell_classes import pyramidal, basket from .params import _long_name, _short_name from copy import deepcopy @@ -376,7 +376,7 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, These drives are spike SOURCES but cells are also targets. External inputs are not targets. """ - type2class = {'L2_pyramidal': Pyr, 'L5_pyramidal': Pyr, + type2class = {'L2_pyramidal': pyramidal, 'L5_pyramidal': pyramidal, 'L2_basket': basket, 'L5_basket': basket} # loop through ALL gids # have to loop over self._gid_list, since this is what we got @@ -389,11 +389,11 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, # figure out which cell type is assoc with the gid # create cells based on loc property if src_type in ('L2_pyramidal', 'L5_pyramidal'): - PyramidalCell = type2class[src_type] + pyramidal_cell = type2class[src_type] # XXX Why doesn't a _Cell have a .threshold? Would make a # lot of sense to include it, as _ArtificialCells do. - cell = PyramidalCell(src_pos, override_params=None, - celltype=src_type, gid=gid) + cell = pyramidal_cell(src_pos, override_params=None, + celltype=src_type, gid=gid) else: basket_cell = type2class[src_type] cell = basket_cell(src_pos, From 9e19f2319ed9b6b85ca393efb9aa9bed20ca28aa Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 30 Apr 2021 23:31:28 -0400 Subject: [PATCH 28/49] ENH: simplify? --- hnn_core/cell_classes.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 908adac34..6b2d8df39 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -93,6 +93,26 @@ def _get_syn_props(p_all, cell_type): } +def _get_basket_syn_props(): + return { + 'ampa': { + 'e': 0, + 'tau1': 0.5, + 'tau2': 5. + }, + 'gabaa': { + 'e': -80, + 'tau1': 0.5, + 'tau2': 5. + }, + 'nmda': { + 'e': 0, + 'tau1': 1., + 'tau2': 20. + } + } + + def _get_mechanisms(p_all, cell_type, section_names, mechanisms): """Get mechanism @@ -157,12 +177,10 @@ def basket(pos, cell_name='L2Basket', gid=None): cell.synapses = dict() # cell._synapse_create() - cell.synapses['soma_ampa'] = cell.syn_create( - cell.soma(0.5), e=0., tau1=0.5, tau2=5.) - cell.synapses['soma_gabaa'] = cell.syn_create( - cell.soma(0.5), e=-80, tau1=0.5, tau2=5.) - cell.synapses['soma_nmda'] = cell.syn_create( - cell.soma(0.5), e=0., tau1=1., tau2=20.) + p_syn = _get_basket_syn_props() + for receptor in p_syn: + cell.synapses[f'soma_{receptor}'] = cell.syn_create( + cell.soma(0.5), **p_syn[receptor]) cell.soma.insert('hh2') From 76f0dd6830bf1a07aa046279f7e18c490658ae37 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 1 May 2021 10:16:29 -0400 Subject: [PATCH 29/49] Introduce p_secs. Must make a class later --- hnn_core/cell.py | 22 ++++++++++++++++++---- hnn_core/cell_classes.py | 40 +++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 2bcd9889b..074efe53c 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -141,7 +141,7 @@ def gid(self, gid): else: raise RuntimeError('Global ID for this cell already assigned!') - def set_biophysics(self, p_mech): + def set_biophysics(self, p_secs): "Set the biophysics for the default Pyramidal cell." # neuron syntax is used to set values for mechanisms @@ -151,10 +151,11 @@ def set_biophysics(self, p_mech): for sec in self.sections: sec_name = sec.name().split('_', 1)[1] - for mech_name in p_mech[sec_name]: + for mech_name in p_secs[sec_name]['mechs']: sec.insert(mech_name) - for attr in p_mech[sec_name][mech_name]: - setattr(sec, attr, p_mech[sec_name][mech_name][attr]) + for attr in p_secs[sec_name]['mechs'][mech_name]: + setattr(sec, attr, + p_secs[sec_name]['mechs'][mech_name][attr]) def create_dends(self, p_dend): """Create dendrites.""" @@ -169,6 +170,19 @@ def create_dends(self, p_dend): self.dends[key].Ra = p_dend[key]['Ra'] self.dends[key].cm = p_dend[key]['cm'] + def create_synapses(self, p_secs, p_syn): + """Create synapses.""" + for sec_name in p_secs: + for receptor in p_secs[sec_name]['syns']: + sec_name_sanitized = sec_name.replace('_', '') + syn_key = f'{sec_name_sanitized}_{receptor}' + if sec_name == 'soma': + seg = self.soma(0.5) + else: + seg = self.dends[sec_name](0.5) + self.synapses[syn_key] = self.syn_create( + seg, **p_syn[receptor]) + def create_soma(self, soma_props): """Create soma and set geometry. diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 6b2d8df39..a6f0ae304 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -168,19 +168,17 @@ def basket(pos, cell_name='L2Basket', gid=None): cell = Cell(pos=pos, gid=gid) cell.name = cell_name - soma_props = _get_basket_soma_props(cell_name) - cell.create_soma(soma_props) + p_secs = dict() + p_secs['soma'] = _get_basket_soma_props(cell_name) + cell.create_soma(p_secs['soma']) cell.sections = [cell.soma] # XXX: needed? cell.secs = _secs_Basket() - cell.set_geometry() - - cell.synapses = dict() - # cell._synapse_create() p_syn = _get_basket_syn_props() - for receptor in p_syn: - cell.synapses[f'soma_{receptor}'] = cell.syn_create( - cell.soma(0.5), **p_syn[receptor]) + p_secs['soma']['syns'] = list(p_syn.keys()) + + cell.set_geometry() + cell.create_synapses(p_secs, p_syn) cell.soma.insert('hh2') @@ -249,8 +247,18 @@ def pyramidal(pos, celltype, override_params=None, gid=None): section_names=section_names, prop_names=prop_names) p_syn = _get_syn_props(p_all, cell.name) + p_secs = p_dend.copy() + p_secs['soma'] = p_soma p_mech = _get_mechanisms(p_all, cell.name, ['soma'] + section_names, mechanisms) + for key in p_secs: + p_secs[key]['mechs'] = p_mech[key] + for key in p_secs: + if key == 'soma': + syns = ['gabaa', 'gabab'] + else: + syns = list(p_syn.keys()) + p_secs[key]['syns'] = syns # Geometry # dend Cm and dend Ra set using soma Cm and soma Ra @@ -274,7 +282,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): cell.dends[key].nseg += 1 # biophysics - cell.set_biophysics(p_mech) + cell.set_biophysics(p_secs) if celltype == 'L5_pyramidal': cell.soma.insert('ar') @@ -306,15 +314,5 @@ def pyramidal(pos, celltype, override_params=None, gid=None): cell.insert_dipole(yscale) # create synapses - cell.synapses['soma_gabaa'] = cell.syn_create(cell.soma(0.5), - **p_syn['gabaa']) - cell.synapses['soma_gabab'] = cell.syn_create(cell.soma(0.5), - **p_syn['gabab']) - - # Dendritic synapses - for sec in section_names: - for receptor in p_syn: - syn_key = sec.replace('_', '') + '_' + receptor - cell.synapses[syn_key] = cell.syn_create( - cell.dends[sec](0.5), **p_syn[receptor]) + cell.create_synapses(p_secs, p_syn) return cell From b47acf955b1ba651aea7ad460b3e61b5e15fce6a Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 1 May 2021 23:54:01 -0400 Subject: [PATCH 30/49] Cosmit: p_secs first, then Neuron code --- hnn_core/cell_classes.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index a6f0ae304..d142671ef 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -170,17 +170,17 @@ def basket(pos, cell_name='L2Basket', gid=None): p_secs = dict() p_secs['soma'] = _get_basket_soma_props(cell_name) + p_syn = _get_basket_syn_props() + p_secs['soma']['syns'] = list(p_syn.keys()) + p_secs['soma']['mechs'] = {'hh2': dict()} + cell.create_soma(p_secs['soma']) cell.sections = [cell.soma] # XXX: needed? cell.secs = _secs_Basket() - p_syn = _get_basket_syn_props() - p_secs['soma']['syns'] = list(p_syn.keys()) - cell.set_geometry() cell.create_synapses(p_secs, p_syn) - - cell.soma.insert('hh2') + cell.set_biophysics(p_secs) if cell_name == 'L2Basket': cell.sect_loc = dict(proximal=['soma'], distal=['soma']) From 1c9b6f3dcfa317b03dfb2f859c757394ea6974fc Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 2 May 2021 00:29:55 -0400 Subject: [PATCH 31/49] ENH: introduce cell.build --- hnn_core/cell.py | 21 +++++++++++++++++++++ hnn_core/cell_classes.py | 35 ++++++----------------------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 074efe53c..7605833a5 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -159,6 +159,8 @@ def set_biophysics(self, p_secs): def create_dends(self, p_dend): """Create dendrites.""" + # sets geom properties based on hoc (2009 model) + # XXX: name should be unique even across cell types? # otherwise Neuron cannot disambiguate, hence # self.name + '_' + key @@ -170,6 +172,14 @@ def create_dends(self, p_dend): self.dends[key].Ra = p_dend[key]['Ra'] self.dends[key].cm = p_dend[key]['cm'] + for key in p_dend: + # set dend nseg + if p_dend[key]['L'] > 100.: + self.dends[key].nseg = int(p_dend[key]['L'] / 50.) + # make dend.nseg odd for all sections + if not self.dends[key].nseg % 2: + self.dends[key].nseg += 1 + def create_synapses(self, p_secs, p_syn): """Create synapses.""" for sec_name in p_secs: @@ -198,6 +208,17 @@ def create_soma(self, soma_props): self.soma.Ra = soma_props['Ra'] self.soma.cm = soma_props['cm'] + def build(self, p_secs, p_syn): + """Build cell in Neuron.""" + self.create_soma(p_secs['soma']) + p_dend = p_secs.copy() + del p_dend['soma'] + self.create_dends(p_dend) # just creates the sections + self.sections = [self.soma] + list(self.dends.values()) + self.set_geometry() + self.create_synapses(p_secs, p_syn) + self.set_biophysics(p_secs) + def move_to_pos(self): """Move cell to position.""" x0 = self.soma.x3d(0) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index d142671ef..e8f66fa9c 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -68,7 +68,7 @@ def _get_basket_soma_props(cell_name): } -def _get_syn_props(p_all, cell_type): +def _get_pyr_syn_props(p_all, cell_type): return { 'ampa': { 'e': p_all['%s_ampa_e' % cell_type], @@ -174,13 +174,9 @@ def basket(pos, cell_name='L2Basket', gid=None): p_secs['soma']['syns'] = list(p_syn.keys()) p_secs['soma']['mechs'] = {'hh2': dict()} - cell.create_soma(p_secs['soma']) - cell.sections = [cell.soma] # XXX: needed? cell.secs = _secs_Basket() - cell.set_geometry() - cell.create_synapses(p_secs, p_syn) - cell.set_biophysics(p_secs) + cell.build(p_secs, p_syn) if cell_name == 'L2Basket': cell.sect_loc = dict(proximal=['soma'], distal=['soma']) @@ -196,6 +192,8 @@ def pyramidal(pos, celltype, override_params=None, gid=None): ---------- pos : tuple Coordinates of cell soma in xyz-space + celltype : str + 'L5_pyramidal' or 'L2_pyramidal'. The pyramidal cell type. override_params : dict or None (optional) Parameters specific to L2 pyramidal neurons to override the default set gid : int or None (optional) @@ -246,7 +244,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): p_dend = _get_dend_props(p_all, cell_type=cell.name, section_names=section_names, prop_names=prop_names) - p_syn = _get_syn_props(p_all, cell.name) + p_syn = _get_pyr_syn_props(p_all, cell.name) p_secs = p_dend.copy() p_secs['soma'] = p_soma p_mech = _get_mechanisms(p_all, cell.name, ['soma'] + section_names, @@ -260,29 +258,10 @@ def pyramidal(pos, celltype, override_params=None, gid=None): syns = list(p_syn.keys()) p_secs[key]['syns'] = syns - # Geometry - # dend Cm and dend Ra set using soma Cm and soma Ra - cell.create_soma(p_soma) - cell.create_dends(p_dend) # just creates the sections - cell.sections = [cell.soma] + list(cell.dends.values()) - cell.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] cell.sect_loc['distal'] = ['apicaltuft'] - # sets geom properties; adjusted after translation from - # hoc (2009 model) - cell.set_geometry() - # resets length,diam,etc. based on param specification - for key in p_dend: - # set dend nseg - if p_dend[key]['L'] > 100.: - cell.dends[key].nseg = int(p_dend[key]['L'] / 50.) - # make dend.nseg odd for all sections - if not cell.dends[key].nseg % 2: - cell.dends[key].nseg += 1 - - # biophysics - cell.set_biophysics(p_secs) + cell.build(p_secs, p_syn) if celltype == 'L5_pyramidal': cell.soma.insert('ar') @@ -313,6 +292,4 @@ def pyramidal(pos, celltype, override_params=None, gid=None): yscale = cell.secs[3] cell.insert_dipole(yscale) - # create synapses - cell.create_synapses(p_secs, p_syn) return cell From 381983b10bf0add8b371104fb9ef190f81020c4e Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 2 May 2021 20:45:48 -0400 Subject: [PATCH 32/49] p_dend should be updated after default geometry is set --- hnn_core/cell.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 7605833a5..d6e69e41b 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -167,18 +167,6 @@ def create_dends(self, p_dend): for key in p_dend: self.dends[key] = h.Section( name=self.name + '_' + key) # create dend - self.dends[key].L = p_dend[key]['L'] - self.dends[key].diam = p_dend[key]['diam'] - self.dends[key].Ra = p_dend[key]['Ra'] - self.dends[key].cm = p_dend[key]['cm'] - - for key in p_dend: - # set dend nseg - if p_dend[key]['L'] > 100.: - self.dends[key].nseg = int(p_dend[key]['L'] / 50.) - # make dend.nseg odd for all sections - if not self.dends[key].nseg % 2: - self.dends[key].nseg += 1 def create_synapses(self, p_secs, p_syn): """Create synapses.""" @@ -215,7 +203,7 @@ def build(self, p_secs, p_syn): del p_dend['soma'] self.create_dends(p_dend) # just creates the sections self.sections = [self.soma] + list(self.dends.values()) - self.set_geometry() + self.set_geometry(p_dend) self.create_synapses(p_secs, p_syn) self.set_biophysics(p_secs) @@ -422,7 +410,7 @@ def _pardistance(self, pos_pre): dy = self.pos[1] - pos_pre[1] return np.sqrt(dx**2 + dy**2) - def set_geometry(self): + def set_geometry(self, p_dend): """Define geometry.""" # Define 3D shape and position of cell. By default neuron uses xy plane # for height and xz plane for depth. This is opposite for model as a @@ -436,6 +424,20 @@ def set_geometry(self): sec.L = sec_lens[sec_name] sec.diam = sec_diams[sec_name] + for sec_name in p_dend: + self.dends[sec_name].Ra = p_dend[sec_name]['Ra'] + self.dends[sec_name].cm = p_dend[sec_name]['cm'] + self.dends[sec_name].L = p_dend[sec_name]['L'] + self.dends[sec_name].diam = p_dend[sec_name]['diam'] + + for key in p_dend: + # set dend nseg + if p_dend[key]['L'] > 100.: + self.dends[key].nseg = int(p_dend[key]['L'] / 50.) + # make dend.nseg odd for all sections + if not self.dends[key].nseg % 2: + self.dends[key].nseg += 1 + if topology is None: topology = list() From e601798328eaaba6e18d6781748a84cb3bcc5f2f Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 2 May 2021 21:15:32 -0400 Subject: [PATCH 33/49] ENH: simplify --- hnn_core/cell.py | 37 ++++++++++++++++++------------------- hnn_core/cell_classes.py | 4 ++-- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index d6e69e41b..17e9aa030 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -203,7 +203,7 @@ def build(self, p_secs, p_syn): del p_dend['soma'] self.create_dends(p_dend) # just creates the sections self.sections = [self.soma] + list(self.dends.values()) - self.set_geometry(p_dend) + self.set_geometry(p_secs) self.create_synapses(p_secs, p_syn) self.set_biophysics(p_secs) @@ -410,33 +410,32 @@ def _pardistance(self, pos_pre): dy = self.pos[1] - pos_pre[1] return np.sqrt(dx**2 + dy**2) - def set_geometry(self, p_dend): + def set_geometry(self, p_secs): """Define geometry.""" # Define 3D shape and position of cell. By default neuron uses xy plane # for height and xz plane for depth. This is opposite for model as a # whole, but convention is followed in this function ease use of gui. - sec_pts, sec_lens, sec_diams, _, topology = self.secs - for sec in self.sections: + sec_pts, _, _, _, topology = self.secs + + for sec_name in p_secs: + if sec_name == 'soma': + sec = self.soma + else: + sec = self.dends[sec_name] + h.pt3dclear(sec=sec) - sec_name = sec.name().split('_', 1)[1] for pt in sec_pts[sec_name]: h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) - sec.L = sec_lens[sec_name] - sec.diam = sec_diams[sec_name] - - for sec_name in p_dend: - self.dends[sec_name].Ra = p_dend[sec_name]['Ra'] - self.dends[sec_name].cm = p_dend[sec_name]['cm'] - self.dends[sec_name].L = p_dend[sec_name]['L'] - self.dends[sec_name].diam = p_dend[sec_name]['diam'] + sec.L = p_secs[sec_name]['L'] + sec.diam = p_secs[sec_name]['diam'] + sec.Ra = p_secs[sec_name]['Ra'] + sec.cm = p_secs[sec_name]['cm'] - for key in p_dend: - # set dend nseg - if p_dend[key]['L'] > 100.: - self.dends[key].nseg = int(p_dend[key]['L'] / 50.) + if sec.L > 100.: + sec.nseg = int(sec.L / 50.) # make dend.nseg odd for all sections - if not self.dends[key].nseg % 2: - self.dends[key].nseg += 1 + if not sec.nseg % 2: + sec.nseg += 1 if topology is None: topology = list() diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index e8f66fa9c..3b6126f50 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -206,7 +206,6 @@ def pyramidal(pos, celltype, override_params=None, gid=None): if celltype == 'L5_pyramidal': p_all_default = get_L5Pyr_params_default() cell.name = 'L5Pyr' - cell.secs = _secs_L5Pyr() # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] mechanisms = { 'hh2': ['gkbar_hh2', 'gnabar_hh2', @@ -220,16 +219,17 @@ def pyramidal(pos, celltype, override_params=None, gid=None): section_names = ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + cell.secs = _secs_L5Pyr() elif celltype == 'L2_pyramidal': p_all_default = get_L2Pyr_params_default() cell.name = 'L2Pyr' - cell.secs = _secs_L2Pyr() mechanisms = { 'km': ['gbar_km'], 'hh2': ['gkbar_hh2', 'gnabar_hh2', 'gl_hh2', 'el_hh2']} section_names = ['apical_trunk', 'apical_1', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] + cell.secs = _secs_L2Pyr() else: raise ValueError(f'Unknown pyramidal cell type: {celltype}') From e6e2c3c6bed888dfb76971422669a4d6e2adb426 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 2 May 2021 22:50:15 -0400 Subject: [PATCH 34/49] ENH: compress into single method create_sections --- doc/api.rst | 5 +-- hnn_core/cell.py | 111 ++++++++++++++++++++--------------------------- 2 files changed, 48 insertions(+), 68 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 2d4f8f4b9..112c4af9a 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -14,12 +14,9 @@ Simulation (:py:mod:`hnn_core`): .. autosummary:: :toctree: generated/ - L2Pyr - L5Pyr - L2Basket - L5Basket simulate_dipole Network + Cell CellResponse Dipole (:py:mod:`hnn_core.dipole`): diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 17e9aa030..dc2979eda 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -157,17 +157,6 @@ def set_biophysics(self, p_secs): setattr(sec, attr, p_secs[sec_name]['mechs'][mech_name][attr]) - def create_dends(self, p_dend): - """Create dendrites.""" - # sets geom properties based on hoc (2009 model) - - # XXX: name should be unique even across cell types? - # otherwise Neuron cannot disambiguate, hence - # self.name + '_' + key - for key in p_dend: - self.dends[key] = h.Section( - name=self.name + '_' + key) # create dend - def create_synapses(self, p_secs, p_syn): """Create synapses.""" for sec_name in p_secs: @@ -181,29 +170,63 @@ def create_synapses(self, p_secs, p_syn): self.synapses[syn_key] = self.syn_create( seg, **p_syn[receptor]) - def create_soma(self, soma_props): + def create_sections(self, p_secs): """Create soma and set geometry. Parameters ---------- - soma_props : dict - The properties of the soma. Must contain + p_secs : dict + The properties of the section. Must contain keys 'L', 'diam', 'Ra', and 'cm'. + + Notes + ----- + By default neuron uses xy plane + for height and xz plane for depth. This is opposite for model as a + whole, but convention is followed in this function ease use of gui. """ - self.soma = h.Section(cell=self, name=self.name + '_soma') - self.soma.L = soma_props['L'] - self.soma.diam = soma_props['diam'] - self.soma.Ra = soma_props['Ra'] - self.soma.cm = soma_props['cm'] + sec_pts, _, _, _, topology = self.secs + + for sec_name in p_secs: + if sec_name == 'soma': + self.soma = h.Section(cell=self, name=self.name + '_soma') + sec = self.soma + else: + self.dends[sec_name] = h.Section( + name=self.name + '_' + sec_name) + sec = self.dends[sec_name] + + h.pt3dclear(sec=sec) + for pt in sec_pts[sec_name]: + h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) + sec.L = p_secs[sec_name]['L'] + sec.diam = p_secs[sec_name]['diam'] + sec.Ra = p_secs[sec_name]['Ra'] + sec.cm = p_secs[sec_name]['cm'] + + if sec.L > 100.: + sec.nseg = int(sec.L / 50.) + # make dend.nseg odd for all sections + if not sec.nseg % 2: + sec.nseg += 1 + + if topology is None: + topology = list() + + # Connects sections of THIS cell together. + for connection in topology: + # XXX: risky to use self.soma as default. Unfortunately there isn't + # a dictionary with all the sections (including soma) + parent_sec = self.dends.get(connection[0], self.soma) + parent_loc = connection[1] + child_sec = self.dends.get(connection[2], self.soma) + child_loc = connection[3] + child_sec.connect(parent_sec, parent_loc, child_loc) def build(self, p_secs, p_syn): """Build cell in Neuron.""" - self.create_soma(p_secs['soma']) - p_dend = p_secs.copy() - del p_dend['soma'] - self.create_dends(p_dend) # just creates the sections + self.create_sections(p_secs) self.sections = [self.soma] + list(self.dends.values()) - self.set_geometry(p_secs) self.create_synapses(p_secs, p_syn) self.set_biophysics(p_secs) @@ -409,43 +432,3 @@ def _pardistance(self, pos_pre): dx = self.pos[0] - pos_pre[0] dy = self.pos[1] - pos_pre[1] return np.sqrt(dx**2 + dy**2) - - def set_geometry(self, p_secs): - """Define geometry.""" - # Define 3D shape and position of cell. By default neuron uses xy plane - # for height and xz plane for depth. This is opposite for model as a - # whole, but convention is followed in this function ease use of gui. - sec_pts, _, _, _, topology = self.secs - - for sec_name in p_secs: - if sec_name == 'soma': - sec = self.soma - else: - sec = self.dends[sec_name] - - h.pt3dclear(sec=sec) - for pt in sec_pts[sec_name]: - h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) - sec.L = p_secs[sec_name]['L'] - sec.diam = p_secs[sec_name]['diam'] - sec.Ra = p_secs[sec_name]['Ra'] - sec.cm = p_secs[sec_name]['cm'] - - if sec.L > 100.: - sec.nseg = int(sec.L / 50.) - # make dend.nseg odd for all sections - if not sec.nseg % 2: - sec.nseg += 1 - - if topology is None: - topology = list() - - # Connects sections of THIS cell together. - for connection in topology: - # XXX: risky to use self.soma as default. Unfortunately there isn't - # a dictionary with all the sections (including soma) - parent_sec = self.dends.get(connection[0], self.soma) - parent_loc = connection[1] - child_sec = self.dends.get(connection[2], self.soma) - child_loc = connection[3] - child_sec.connect(parent_sec, parent_loc, child_loc) From f79a1574160e0caaf0db008be367c5a019bd7882 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 2 May 2021 22:53:31 -0400 Subject: [PATCH 35/49] FIX: address comments by chris --- hnn_core/cell_classes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 3b6126f50..4b7c30f10 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -49,8 +49,8 @@ def _get_dend_props(params, cell_type, section_names, prop_names): return dend_props -def _get_soma_props(p_all, cell_type): - """Hardcoded somatic properties.""" +def _get_pyr_soma_props(p_all, cell_type): + """Get somatic properties.""" return { 'L': p_all[f'{cell_type}_soma_L'], 'diam': p_all[f'{cell_type}_soma_diam'], @@ -149,7 +149,7 @@ def _get_mechanisms(p_all, cell_type, section_names, mechanisms): def basket(pos, cell_name='L2Basket', gid=None): - """Get layer 2 basket cells. + """Get layer 2 / layer 5 basket cells. Parameters ---------- @@ -240,7 +240,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): prop_names = ['L', 'diam', 'Ra', 'cm'] # Get somatic, dendritic, and synapse properties - p_soma = _get_soma_props(p_all, cell.name) + p_soma = _get_pyr_soma_props(p_all, cell.name) p_dend = _get_dend_props(p_all, cell_type=cell.name, section_names=section_names, prop_names=prop_names) From 0ab24a8e60058cd7669e125c20ad20ed8841a750 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 3 May 2021 12:33:16 -0400 Subject: [PATCH 36/49] ENH: bit more simplification --- hnn_core/cell_classes.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index 4b7c30f10..d9a0bbb83 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -176,12 +176,13 @@ def basket(pos, cell_name='L2Basket', gid=None): cell.secs = _secs_Basket() - cell.build(p_secs, p_syn) - if cell_name == 'L2Basket': cell.sect_loc = dict(proximal=['soma'], distal=['soma']) elif cell_name == 'L5Basket': cell.sect_loc = dict(proximal=['soma'], distal=[]) + + cell.build(p_secs, p_syn) + return cell @@ -214,7 +215,8 @@ def pyramidal(pos, celltype, override_params=None, gid=None): 'cad': ['taur_cad'], 'kca': ['gbar_kca'], 'km': ['gbar_km'], - 'cat': ['gbar_cat'] + 'cat': ['gbar_cat'], + 'ar': ['gbar_ar'] } section_names = ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', @@ -264,13 +266,6 @@ def pyramidal(pos, celltype, override_params=None, gid=None): cell.build(p_secs, p_syn) if celltype == 'L5_pyramidal': - cell.soma.insert('ar') - cell.soma.gbar_ar = p_all['L5Pyr_soma_gbar_ar'] - - # set dend biophysics not specified in Pyr() - for key in cell.dends: - # insert 'ar' mechanism - cell.dends[key].insert('ar') # set gbar_ar # Value depends on distance from the soma. Soma is set as From f15693d28bca4c7da18c112104c88e92ec0cbe67 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 3 May 2021 21:59:53 -0400 Subject: [PATCH 37/49] ENH: allow callable in mech value --- hnn_core/cell.py | 20 ++++++++++++++++---- hnn_core/cell_classes.py | 22 +++------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index dc2979eda..89deed4fa 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -149,13 +149,25 @@ def set_biophysics(self, p_secs): # in a section. This method is significantly faster than using # a for loop to iterate over all segments to set mech values + # Value depends on distance from the soma. Soma is set as + # origin by passing cell.soma as a sec argument to h.distance() + # Then iterate over segment nodes of dendritic sections + # and set gbar_ar depending on h.distance(seg.x), which returns + # distance from the soma to this point on the CURRENTLY ACCESSED + # SECTION!!! + h.distance(sec=self.soma) for sec in self.sections: sec_name = sec.name().split('_', 1)[1] - for mech_name in p_secs[sec_name]['mechs']: + for mech_name, mech in p_secs[sec_name]['mechs'].items(): sec.insert(mech_name) - for attr in p_secs[sec_name]['mechs'][mech_name]: - setattr(sec, attr, - p_secs[sec_name]['mechs'][mech_name][attr]) + for attr, val in mech.items(): + if hasattr(val, '__call__'): + sec.push() + for seg in sec: + setattr(seg, attr, val(h.distance(seg.x))) + h.pop_section() + else: + setattr(sec, attr, val) def create_synapses(self, p_secs, p_syn): """Create synapses.""" diff --git a/hnn_core/cell_classes.py b/hnn_core/cell_classes.py index d9a0bbb83..fba96d9f0 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cell_classes.py @@ -253,11 +253,13 @@ def pyramidal(pos, celltype, override_params=None, gid=None): mechanisms) for key in p_secs: p_secs[key]['mechs'] = p_mech[key] - for key in p_secs: if key == 'soma': syns = ['gabaa', 'gabab'] else: syns = list(p_syn.keys()) + if celltype == 'L5_pyramidal': + p_secs[key]['mechs'][ + 'ar']['gbar_ar'] = lambda x: 1e-6 * np.exp(3e-3 * x) p_secs[key]['syns'] = syns cell.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] @@ -265,24 +267,6 @@ def pyramidal(pos, celltype, override_params=None, gid=None): cell.build(p_secs, p_syn) - if celltype == 'L5_pyramidal': - - # set gbar_ar - # Value depends on distance from the soma. Soma is set as - # origin by passing cell.soma as a sec argument to h.distance() - # Then iterate over segment nodes of dendritic sections - # and set gbar_ar depending on h.distance(seg.x), which returns - # distance from the soma to this point on the CURRENTLY ACCESSED - # SECTION!!! - h.distance(sec=cell.soma) - - for key in cell.dends: - cell.dends[key].push() - for seg in cell.dends[key]: - seg.gbar_ar = 1e-6 * np.exp(3e-3 * h.distance(seg.x)) - - h.pop_section() - # insert dipole yscale = cell.secs[3] cell.insert_dipole(yscale) From d2a671dd0edf8b7e270427e15f65972ae723375a Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 3 May 2021 22:19:16 -0400 Subject: [PATCH 38/49] DOC: better documentation --- hnn_core/cell.py | 54 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 89deed4fa..431b9afe1 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -141,7 +141,7 @@ def gid(self, gid): else: raise RuntimeError('Global ID for this cell already assigned!') - def set_biophysics(self, p_secs): + def _set_biophysics(self, p_secs): "Set the biophysics for the default Pyramidal cell." # neuron syntax is used to set values for mechanisms @@ -169,7 +169,7 @@ def set_biophysics(self, p_secs): else: setattr(sec, attr, val) - def create_synapses(self, p_secs, p_syn): + def _create_synapses(self, p_secs, p_syn): """Create synapses.""" for sec_name in p_secs: for receptor in p_secs[sec_name]['syns']: @@ -182,15 +182,9 @@ def create_synapses(self, p_secs, p_syn): self.synapses[syn_key] = self.syn_create( seg, **p_syn[receptor]) - def create_sections(self, p_secs): + def _create_sections(self, p_secs): """Create soma and set geometry. - Parameters - ---------- - p_secs : dict - The properties of the section. Must contain - keys 'L', 'diam', 'Ra', and 'cm'. - Notes ----- By default neuron uses xy plane @@ -236,11 +230,45 @@ def create_sections(self, p_secs): child_sec.connect(parent_sec, parent_loc, child_loc) def build(self, p_secs, p_syn): - """Build cell in Neuron.""" - self.create_sections(p_secs) + """Build cell in Neuron. + + Parameters + ---------- + p_secs : dict + Dictionary with keys as section name. + p_secs[sec_name] is a dictionary with keys + L, diam, Ra, cm, syns and mech. + syns is a list specifying the synapses at that section. + The properties of syn are specified in p_syn. + mech is a dict with keys as the mechanism names. The + values are dictionaries with properties of the mechanism. + p_syn : dict of dict + Keys are name of synaptic mechanism. Each synaptic mechanism + has keys for parameters of the mechanism, e.g., 'e', 'tau1', + 'tau2'. + + Examples + -------- + p_secs = { + 'soma': + { + 'L': 39, + 'diam': 20, + 'cm': 0.85, + 'Ra': 200., + 'syns': ['ampa', 'gabaa', 'nmda'], + 'mechs' : { + 'ca': { + 'gbar_ca': 60 + } + } + } + } + """ + self._create_sections(p_secs) self.sections = [self.soma] + list(self.dends.values()) - self.create_synapses(p_secs, p_syn) - self.set_biophysics(p_secs) + self._create_synapses(p_secs, p_syn) + self._set_biophysics(p_secs) def move_to_pos(self): """Move cell to position.""" From 7a2fa566ba929eb49413380d5a5db16f59c3a3fd Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 3 May 2021 22:30:02 -0400 Subject: [PATCH 39/49] ENH: rename cell_classes to cells_default.py --- hnn_core/__init__.py | 2 +- hnn_core/{cell_classes.py => cells_default.py} | 2 +- hnn_core/network_builder.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename hnn_core/{cell_classes.py => cells_default.py} (99%) diff --git a/hnn_core/__init__.py b/hnn_core/__init__.py index 35d1222ff..b204822ae 100644 --- a/hnn_core/__init__.py +++ b/hnn_core/__init__.py @@ -3,7 +3,7 @@ from .params import Params, read_params from .network import Network from .cell_response import CellResponse, read_spikes -from .cell_classes import pyramidal, basket +from .cells_default import pyramidal, basket from .parallel_backends import MPIBackend, JoblibBackend __version__ = '0.2.dev0' diff --git a/hnn_core/cell_classes.py b/hnn_core/cells_default.py similarity index 99% rename from hnn_core/cell_classes.py rename to hnn_core/cells_default.py index fba96d9f0..5ba69999a 100644 --- a/hnn_core/cell_classes.py +++ b/hnn_core/cells_default.py @@ -1,4 +1,4 @@ -"""Model for Pyramidal cell class.""" +"""Default cell models.""" # Authors: Mainak Jas # Sam Neymotin diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index f01a081e6..9d0607761 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -13,7 +13,7 @@ h.nrnunit_use_legacy(1) from .cell import _ArtificialCell -from .cell_classes import pyramidal, basket +from .cells_default import pyramidal, basket from .params import _long_name, _short_name from copy import deepcopy From 9fd39bc321309e40b71b0a7a52c4dce555ddbe33 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 4 May 2021 16:13:44 -0400 Subject: [PATCH 40/49] MAINT: more failsafe connection of sections --- hnn_core/cell.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 431b9afe1..83cf84fcc 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -85,9 +85,14 @@ class Cell: ---------- pos : list of length 3 The position of the cell. + soma : instance of h.Section | None + The soma of the cell instantiated in Neuron. If None, + the cell has not yet been built in Neuron. dends : dict The dendrites. The key is the name of the dendrite and the value is an instance of h.Section. + sections : list of h.Section + All the sections including soma and dendrites. synapses : dict The synapses that the cell can use for connections. dipole_pp : list of h.Dipole() @@ -111,7 +116,7 @@ class Cell: def __init__(self, pos, gid=None): self.pos = pos - # preallocate dict to store dends + self.soma = None self.dends = dict() self.synapses = dict() self.sect_loc = dict() @@ -132,6 +137,12 @@ def __repr__(self): def gid(self): return self._gid + @property + def sections(self): + if self.soma is None: + raise ValueError('Cell has not yet been built in Neuron') + return [self.soma] + list(self.dends.values()) + @gid.setter def gid(self, gid): if not isinstance(gid, int): @@ -210,7 +221,7 @@ def _create_sections(self, p_secs): sec.Ra = p_secs[sec_name]['Ra'] sec.cm = p_secs[sec_name]['cm'] - if sec.L > 100.: + if sec.L > 100.: # 100 um sec.nseg = int(sec.L / 50.) # make dend.nseg odd for all sections if not sec.nseg % 2: @@ -221,11 +232,15 @@ def _create_sections(self, p_secs): # Connects sections of THIS cell together. for connection in topology: - # XXX: risky to use self.soma as default. Unfortunately there isn't - # a dictionary with all the sections (including soma) - parent_sec = self.dends.get(connection[0], self.soma) + if connection[0] == 'soma': + parent_sec = self.soma + else: + parent_sec = self.dends[connection[0]] + if connection[2] == 'soma': + child_sec = self.soma + else: + child_sec = self.dends[connection[2]] parent_loc = connection[1] - child_sec = self.dends.get(connection[2], self.soma) child_loc = connection[3] child_sec.connect(parent_sec, parent_loc, child_loc) @@ -266,7 +281,6 @@ def build(self, p_secs, p_syn): } """ self._create_sections(p_secs) - self.sections = [self.soma] + list(self.dends.values()) self._create_synapses(p_secs, p_syn) self._set_biophysics(p_secs) From bb763cfd7b2b9857f0e712d4bb1f0d358daa1bec Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 4 May 2021 16:57:49 -0400 Subject: [PATCH 41/49] MAINT: no more self.secs --- hnn_core/cell.py | 18 ++++++++++++------ hnn_core/cells_default.py | 17 +++++++++++------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 83cf84fcc..e763efc64 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -193,7 +193,7 @@ def _create_synapses(self, p_secs, p_syn): self.synapses[syn_key] = self.syn_create( seg, **p_syn[receptor]) - def _create_sections(self, p_secs): + def _create_sections(self, p_secs, topology): """Create soma and set geometry. Notes @@ -202,8 +202,6 @@ def _create_sections(self, p_secs): for height and xz plane for depth. This is opposite for model as a whole, but convention is followed in this function ease use of gui. """ - sec_pts, _, _, _, topology = self.secs - for sec_name in p_secs: if sec_name == 'soma': self.soma = h.Section(cell=self, name=self.name + '_soma') @@ -214,7 +212,7 @@ def _create_sections(self, p_secs): sec = self.dends[sec_name] h.pt3dclear(sec=sec) - for pt in sec_pts[sec_name]: + for pt in p_secs[sec_name]['sec_pts']: h.pt3dadd(pt[0], pt[1], pt[2], 1, sec=sec) sec.L = p_secs[sec_name]['L'] sec.diam = p_secs[sec_name]['diam'] @@ -244,7 +242,7 @@ def _create_sections(self, p_secs): child_loc = connection[3] child_sec.connect(parent_sec, parent_loc, child_loc) - def build(self, p_secs, p_syn): + def build(self, p_secs, p_syn, topology): """Build cell in Neuron. Parameters @@ -261,6 +259,14 @@ def build(self, p_secs, p_syn): Keys are name of synaptic mechanism. Each synaptic mechanism has keys for parameters of the mechanism, e.g., 'e', 'tau1', 'tau2'. + topology : list of list + The topology of cell sections. Each element is a list of + 4 items in the format + [parent_sec, parent_loc, child_sec, child_loc] where + parent_sec and parent_loc are float between 0 and 1 + specifying the location in the section to connect and + parent_sec and child_sec are names of the connecting + sections. Examples -------- @@ -280,7 +286,7 @@ def build(self, p_secs, p_syn): } } """ - self._create_sections(p_secs) + self._create_sections(p_secs, topology) self._create_synapses(p_secs, p_syn) self._set_biophysics(p_secs) diff --git a/hnn_core/cells_default.py b/hnn_core/cells_default.py index 5ba69999a..45acb715e 100644 --- a/hnn_core/cells_default.py +++ b/hnn_core/cells_default.py @@ -174,14 +174,16 @@ def basket(pos, cell_name='L2Basket', gid=None): p_secs['soma']['syns'] = list(p_syn.keys()) p_secs['soma']['mechs'] = {'hh2': dict()} - cell.secs = _secs_Basket() + sec_pts, _, _, _, topology = _secs_Basket() + for sec_name in p_secs: + p_secs[sec_name]['sec_pts'] = sec_pts[sec_name] if cell_name == 'L2Basket': cell.sect_loc = dict(proximal=['soma'], distal=['soma']) elif cell_name == 'L5Basket': cell.sect_loc = dict(proximal=['soma'], distal=[]) - cell.build(p_secs, p_syn) + cell.build(p_secs, p_syn, topology) return cell @@ -221,7 +223,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): section_names = ['apical_trunk', 'apical_1', 'apical_2', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - cell.secs = _secs_L5Pyr() + sec_pts, _, _, yscale, topology = _secs_L5Pyr() elif celltype == 'L2_pyramidal': p_all_default = get_L2Pyr_params_default() cell.name = 'L2Pyr' @@ -231,7 +233,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): 'gl_hh2', 'el_hh2']} section_names = ['apical_trunk', 'apical_1', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] - cell.secs = _secs_L2Pyr() + sec_pts, _, _, yscale, topology = _secs_L2Pyr() else: raise ValueError(f'Unknown pyramidal cell type: {celltype}') @@ -262,13 +264,16 @@ def pyramidal(pos, celltype, override_params=None, gid=None): 'ar']['gbar_ar'] = lambda x: 1e-6 * np.exp(3e-3 * x) p_secs[key]['syns'] = syns + for sec_name in p_secs: + p_secs[sec_name]['sec_pts'] = sec_pts[sec_name] + cell.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] cell.sect_loc['distal'] = ['apicaltuft'] - cell.build(p_secs, p_syn) + cell.build(p_secs, p_syn, topology) # insert dipole - yscale = cell.secs[3] + yscale = yscale cell.insert_dipole(yscale) return cell From 495816768d640a77fe32bf9f2588cb4697884db3 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 4 May 2021 17:23:09 -0400 Subject: [PATCH 42/49] ENH: sec_pts is documented + sect_loc is updated transparently? --- hnn_core/cell.py | 12 ++++++++++-- hnn_core/cells_default.py | 30 ++++++++++++++---------------- hnn_core/tests/test_cell.py | 8 ++++---- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index e763efc64..eff90fa77 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -74,6 +74,8 @@ class Cell: Parameters ---------- + name : str + The name of the cell. pos : tuple The (x, y, z) coordinates. gid : int or None (optional) @@ -114,7 +116,8 @@ class Cell: names of section locations that are proximal or distal. """ - def __init__(self, pos, gid=None): + def __init__(self, name, pos, gid=None): + self.name = name self.pos = pos self.soma = None self.dends = dict() @@ -242,7 +245,7 @@ def _create_sections(self, p_secs, topology): child_loc = connection[3] child_sec.connect(parent_sec, parent_loc, child_loc) - def build(self, p_secs, p_syn, topology): + def build(self, p_secs, p_syn, topology, sect_loc): """Build cell in Neuron. Parameters @@ -267,6 +270,9 @@ def build(self, p_secs, p_syn, topology): specifying the location in the section to connect and parent_sec and child_sec are names of the connecting sections. + sect_loc : dict of list + Can have keys 'proximal' or 'distal' each containing + names of section locations that are proximal or distal. Examples -------- @@ -277,6 +283,7 @@ def build(self, p_secs, p_syn, topology): 'diam': 20, 'cm': 0.85, 'Ra': 200., + 'sec_pts': [[0, 0, 0], [0, 39., 0]], 'syns': ['ampa', 'gabaa', 'nmda'], 'mechs' : { 'ca': { @@ -289,6 +296,7 @@ def build(self, p_secs, p_syn, topology): self._create_sections(p_secs, topology) self._create_synapses(p_secs, p_syn) self._set_biophysics(p_secs) + self.sect_loc = sect_loc def move_to_pos(self): """Move cell to position.""" diff --git a/hnn_core/cells_default.py b/hnn_core/cells_default.py index 45acb715e..3392839dc 100644 --- a/hnn_core/cells_default.py +++ b/hnn_core/cells_default.py @@ -165,9 +165,6 @@ def basket(pos, cell_name='L2Basket', gid=None): cell : instance of BasketSingle The basket cell. """ - cell = Cell(pos=pos, gid=gid) - cell.name = cell_name - p_secs = dict() p_secs['soma'] = _get_basket_soma_props(cell_name) p_syn = _get_basket_syn_props() @@ -179,11 +176,12 @@ def basket(pos, cell_name='L2Basket', gid=None): p_secs[sec_name]['sec_pts'] = sec_pts[sec_name] if cell_name == 'L2Basket': - cell.sect_loc = dict(proximal=['soma'], distal=['soma']) + sect_loc = dict(proximal=['soma'], distal=['soma']) elif cell_name == 'L5Basket': - cell.sect_loc = dict(proximal=['soma'], distal=[]) + sect_loc = dict(proximal=['soma'], distal=[]) - cell.build(p_secs, p_syn, topology) + cell = Cell(cell_name, pos=pos, gid=gid) + cell.build(p_secs, p_syn, topology, sect_loc) return cell @@ -205,10 +203,9 @@ def pyramidal(pos, celltype, override_params=None, gid=None): yet attached to a network. Once the GID is set, it cannot be changed. """ - cell = Cell(pos=pos, gid=gid) if celltype == 'L5_pyramidal': p_all_default = get_L5Pyr_params_default() - cell.name = 'L5Pyr' + cell_name = 'L5Pyr' # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] mechanisms = { 'hh2': ['gkbar_hh2', 'gnabar_hh2', @@ -226,7 +223,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): sec_pts, _, _, yscale, topology = _secs_L5Pyr() elif celltype == 'L2_pyramidal': p_all_default = get_L2Pyr_params_default() - cell.name = 'L2Pyr' + cell_name = 'L2Pyr' mechanisms = { 'km': ['gbar_km'], 'hh2': ['gkbar_hh2', 'gnabar_hh2', @@ -244,14 +241,14 @@ def pyramidal(pos, celltype, override_params=None, gid=None): prop_names = ['L', 'diam', 'Ra', 'cm'] # Get somatic, dendritic, and synapse properties - p_soma = _get_pyr_soma_props(p_all, cell.name) - p_dend = _get_dend_props(p_all, cell_type=cell.name, + p_soma = _get_pyr_soma_props(p_all, cell_name) + p_dend = _get_dend_props(p_all, cell_type=cell_name, section_names=section_names, prop_names=prop_names) - p_syn = _get_pyr_syn_props(p_all, cell.name) + p_syn = _get_pyr_syn_props(p_all, cell_name) p_secs = p_dend.copy() p_secs['soma'] = p_soma - p_mech = _get_mechanisms(p_all, cell.name, ['soma'] + section_names, + p_mech = _get_mechanisms(p_all, cell_name, ['soma'] + section_names, mechanisms) for key in p_secs: p_secs[key]['mechs'] = p_mech[key] @@ -267,10 +264,11 @@ def pyramidal(pos, celltype, override_params=None, gid=None): for sec_name in p_secs: p_secs[sec_name]['sec_pts'] = sec_pts[sec_name] - cell.sect_loc['proximal'] = ['apicaloblique', 'basal2', 'basal3'] - cell.sect_loc['distal'] = ['apicaltuft'] + sect_loc = {'proximal': ['apicaloblique', 'basal2', 'basal3'], + 'distal': ['apicaltuft']} - cell.build(p_secs, p_syn, topology) + cell = Cell(name=cell_name, pos=pos, gid=gid) + cell.build(p_secs, p_syn, topology, sect_loc=sect_loc) # insert dipole yscale = yscale diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index 23172d763..69901013d 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -11,16 +11,16 @@ def test_cell(): """Test cells object.""" pos = (0., 0., 0.) - + name = 'test' # GID is assigned exactly once for each cell, either at initialisation... - cell = Cell(pos=pos, gid=42) + cell = Cell(name, pos=pos, gid=42) assert cell.gid == 42 with pytest.raises(RuntimeError, match='Global ID for this cell already assigned!'): cell.gid += 1 # ... or later # cells can exist fine without gid - cell = Cell(pos=pos) + cell = Cell(name, pos=pos) assert cell.gid is None # check that it's initialised to None with pytest.raises(ValueError, match='gid must be an integer'): @@ -30,7 +30,7 @@ def test_cell(): with pytest.raises(ValueError, match='gid must be an integer'): # test init checks gid - cell = Cell(pos=pos, gid='one') + cell = Cell(name, pos=pos, gid='one') # test that ExpSyn always takes nrn.Segment, not float with pytest.raises(TypeError, match='secloc must be instance of'): From 2ff530fde5436aec2fa83abbfce4912a5c87f273 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 08:13:07 -0400 Subject: [PATCH 43/49] FIX some quick fixes --- hnn_core/cells_default.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hnn_core/cells_default.py b/hnn_core/cells_default.py index 3392839dc..90bd603ce 100644 --- a/hnn_core/cells_default.py +++ b/hnn_core/cells_default.py @@ -5,8 +5,6 @@ import numpy as np -from neuron import h - from .cell import Cell from .params import compare_dictionaries @@ -155,6 +153,8 @@ def basket(pos, cell_name='L2Basket', gid=None): ---------- pos : tuple Coordinates of cell soma in xyz-space + cell_name : str + The name of the cell. gid : int or None (optional) Each cell in a network is uniquely identified by it's "global ID": GID. The GID is an integer from 0 to n_cells, or None if the cell is not @@ -271,7 +271,6 @@ def pyramidal(pos, celltype, override_params=None, gid=None): cell.build(p_secs, p_syn, topology, sect_loc=sect_loc) # insert dipole - yscale = yscale cell.insert_dipole(yscale) return cell From 9557f02454453c28365d7e5fa9df1a455b93d014 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 12:41:48 -0400 Subject: [PATCH 44/49] MAINT: unify celltype, cell_type, and cell_name --- hnn_core/cell.py | 4 ++-- hnn_core/cells_default.py | 28 ++++++++++++++-------------- hnn_core/network_builder.py | 3 ++- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index eff90fa77..9789f39fe 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -156,14 +156,14 @@ def gid(self, gid): raise RuntimeError('Global ID for this cell already assigned!') def _set_biophysics(self, p_secs): - "Set the biophysics for the default Pyramidal cell." + "Set the biophysics for the cell." # neuron syntax is used to set values for mechanisms # sec.gbar_mech = x sets value of gbar for mech to x for all segs # in a section. This method is significantly faster than using # a for loop to iterate over all segments to set mech values - # Value depends on distance from the soma. Soma is set as + # If value depends on distance from the soma. Soma is set as # origin by passing cell.soma as a sec argument to h.distance() # Then iterate over segment nodes of dendritic sections # and set gbar_ar depending on h.distance(seg.x), which returns diff --git a/hnn_core/cells_default.py b/hnn_core/cells_default.py index 90bd603ce..4227475a5 100644 --- a/hnn_core/cells_default.py +++ b/hnn_core/cells_default.py @@ -165,6 +165,13 @@ def basket(pos, cell_name='L2Basket', gid=None): cell : instance of BasketSingle The basket cell. """ + if cell_name == 'L2Basket': + sect_loc = dict(proximal=['soma'], distal=['soma']) + elif cell_name == 'L5Basket': + sect_loc = dict(proximal=['soma'], distal=[]) + else: + raise ValueError(f'Unknown basket cell type: {cell_name}') + p_secs = dict() p_secs['soma'] = _get_basket_soma_props(cell_name) p_syn = _get_basket_syn_props() @@ -175,26 +182,21 @@ def basket(pos, cell_name='L2Basket', gid=None): for sec_name in p_secs: p_secs[sec_name]['sec_pts'] = sec_pts[sec_name] - if cell_name == 'L2Basket': - sect_loc = dict(proximal=['soma'], distal=['soma']) - elif cell_name == 'L5Basket': - sect_loc = dict(proximal=['soma'], distal=[]) - cell = Cell(cell_name, pos=pos, gid=gid) cell.build(p_secs, p_syn, topology, sect_loc) return cell -def pyramidal(pos, celltype, override_params=None, gid=None): +def pyramidal(pos, cell_name, override_params=None, gid=None): """Pyramidal neuron. Parameters ---------- pos : tuple Coordinates of cell soma in xyz-space - celltype : str - 'L5_pyramidal' or 'L2_pyramidal'. The pyramidal cell type. + cell_name : str + 'L5Pyr' or 'L2Pyr'. The pyramidal cell type. override_params : dict or None (optional) Parameters specific to L2 pyramidal neurons to override the default set gid : int or None (optional) @@ -203,9 +205,8 @@ def pyramidal(pos, celltype, override_params=None, gid=None): yet attached to a network. Once the GID is set, it cannot be changed. """ - if celltype == 'L5_pyramidal': + if cell_name == 'L5Pyr': p_all_default = get_L5Pyr_params_default() - cell_name = 'L5Pyr' # units = ['pS/um^2', 'S/cm^2', 'pS/um^2', '??', 'tau', '??'] mechanisms = { 'hh2': ['gkbar_hh2', 'gnabar_hh2', @@ -221,9 +222,8 @@ def pyramidal(pos, celltype, override_params=None, gid=None): 'apical_2', 'apical_tuft', 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] sec_pts, _, _, yscale, topology = _secs_L5Pyr() - elif celltype == 'L2_pyramidal': + elif cell_name == 'L2Pyr': p_all_default = get_L2Pyr_params_default() - cell_name = 'L2Pyr' mechanisms = { 'km': ['gbar_km'], 'hh2': ['gkbar_hh2', 'gnabar_hh2', @@ -232,7 +232,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): 'apical_oblique', 'basal_1', 'basal_2', 'basal_3'] sec_pts, _, _, yscale, topology = _secs_L2Pyr() else: - raise ValueError(f'Unknown pyramidal cell type: {celltype}') + raise ValueError(f'Unknown pyramidal cell type: {cell_name}') p_all = p_all_default if override_params is not None: @@ -256,7 +256,7 @@ def pyramidal(pos, celltype, override_params=None, gid=None): syns = ['gabaa', 'gabab'] else: syns = list(p_syn.keys()) - if celltype == 'L5_pyramidal': + if cell_name == 'L5Pyr': p_secs[key]['mechs'][ 'ar']['gbar_ar'] = lambda x: 1e-6 * np.exp(3e-3 * x) p_secs[key]['syns'] = syns diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 9d0607761..2ac11887d 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -393,7 +393,8 @@ def _create_cells_and_drives(self, threshold, record_vsoma=False, # XXX Why doesn't a _Cell have a .threshold? Would make a # lot of sense to include it, as _ArtificialCells do. cell = pyramidal_cell(src_pos, override_params=None, - celltype=src_type, gid=gid) + cell_name=_short_name(src_type), + gid=gid) else: basket_cell = type2class[src_type] cell = basket_cell(src_pos, From cefc4343ef534927b64cbef742bcddc4fbd6460b Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 15:30:06 -0400 Subject: [PATCH 45/49] Clearer code? --- hnn_core/cell.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 9789f39fe..123bcc867 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -166,15 +166,18 @@ def _set_biophysics(self, p_secs): # If value depends on distance from the soma. Soma is set as # origin by passing cell.soma as a sec argument to h.distance() # Then iterate over segment nodes of dendritic sections - # and set gbar_ar depending on h.distance(seg.x), which returns + # and set attribute depending on h.distance(seg.x), which returns # distance from the soma to this point on the CURRENTLY ACCESSED # SECTION!!! h.distance(sec=self.soma) - for sec in self.sections: - sec_name = sec.name().split('_', 1)[1] - for mech_name, mech in p_secs[sec_name]['mechs'].items(): + for sec_name, p_sec in p_secs.items(): + if sec_name == 'soma': + sec = self.soma + else: + sec = self.dends[sec_name] + for mech_name, p_mech in p_sec['mechs'].items(): sec.insert(mech_name) - for attr, val in mech.items(): + for attr, val in p_mech.items(): if hasattr(val, '__call__'): sec.push() for seg in sec: From 6b76eeca5b3f967b14d0a33ba5072c6a41a08204 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 15:48:05 -0400 Subject: [PATCH 46/49] MAINT: get rid of self.soma --- hnn_core/cell.py | 72 ++++++++++++------------------------- hnn_core/network_builder.py | 2 +- 2 files changed, 24 insertions(+), 50 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 123bcc867..22a94792e 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -1,6 +1,6 @@ """Establish class def for general cell features.""" -# Authors: Mainak Jas +# Authors: Mainak Jas # Sam Neymotin import numpy as np @@ -87,14 +87,9 @@ class Cell: ---------- pos : list of length 3 The position of the cell. - soma : instance of h.Section | None - The soma of the cell instantiated in Neuron. If None, - the cell has not yet been built in Neuron. - dends : dict - The dendrites. The key is the name of the dendrite + sections : dict + The sections. The key is the name of the dendrite and the value is an instance of h.Section. - sections : list of h.Section - All the sections including soma and dendrites. synapses : dict The synapses that the cell can use for connections. dipole_pp : list of h.Dipole() @@ -119,8 +114,7 @@ class Cell: def __init__(self, name, pos, gid=None): self.name = name self.pos = pos - self.soma = None - self.dends = dict() + self.sections = dict() self.synapses = dict() self.sect_loc = dict() self.rec_v = h.Vector() @@ -140,12 +134,6 @@ def __repr__(self): def gid(self): return self._gid - @property - def sections(self): - if self.soma is None: - raise ValueError('Cell has not yet been built in Neuron') - return [self.soma] + list(self.dends.values()) - @gid.setter def gid(self, gid): if not isinstance(gid, int): @@ -169,12 +157,9 @@ def _set_biophysics(self, p_secs): # and set attribute depending on h.distance(seg.x), which returns # distance from the soma to this point on the CURRENTLY ACCESSED # SECTION!!! - h.distance(sec=self.soma) + h.distance(sec=self.sections['soma']) for sec_name, p_sec in p_secs.items(): - if sec_name == 'soma': - sec = self.soma - else: - sec = self.dends[sec_name] + sec = self.sections[sec_name] for mech_name, p_mech in p_sec['mechs'].items(): sec.insert(mech_name) for attr, val in p_mech.items(): @@ -192,10 +177,7 @@ def _create_synapses(self, p_secs, p_syn): for receptor in p_secs[sec_name]['syns']: sec_name_sanitized = sec_name.replace('_', '') syn_key = f'{sec_name_sanitized}_{receptor}' - if sec_name == 'soma': - seg = self.soma(0.5) - else: - seg = self.dends[sec_name](0.5) + seg = self.sections[sec_name](0.5) self.synapses[syn_key] = self.syn_create( seg, **p_syn[receptor]) @@ -208,14 +190,11 @@ def _create_sections(self, p_secs, topology): for height and xz plane for depth. This is opposite for model as a whole, but convention is followed in this function ease use of gui. """ + if 'soma' not in p_secs: + raise KeyError('soma must be defined for cell') for sec_name in p_secs: - if sec_name == 'soma': - self.soma = h.Section(cell=self, name=self.name + '_soma') - sec = self.soma - else: - self.dends[sec_name] = h.Section( - name=self.name + '_' + sec_name) - sec = self.dends[sec_name] + sec = h.Section(name=f'{self.name}_{sec_name}') + self.sections[sec_name] = sec h.pt3dclear(sec=sec) for pt in p_secs[sec_name]['sec_pts']: @@ -236,14 +215,8 @@ def _create_sections(self, p_secs, topology): # Connects sections of THIS cell together. for connection in topology: - if connection[0] == 'soma': - parent_sec = self.soma - else: - parent_sec = self.dends[connection[0]] - if connection[2] == 'soma': - child_sec = self.soma - else: - child_sec = self.dends[connection[2]] + parent_sec = self.sections[connection[0]] + child_sec = self.sections[connection[2]] parent_loc = connection[1] child_loc = connection[3] child_sec.connect(parent_sec, parent_loc, child_loc) @@ -303,14 +276,14 @@ def build(self, p_secs, p_syn, topology, sect_loc): def move_to_pos(self): """Move cell to position.""" - x0 = self.soma.x3d(0) - y0 = self.soma.y3d(0) - z0 = self.soma.z3d(0) + x0 = self.sections['soma'].x3d(0) + y0 = self.sections['soma'].y3d(0) + z0 = self.sections['soma'].z3d(0) dx = self.pos[0] * 100 - x0 dy = self.pos[2] - y0 dz = self.pos[1] * 100 - z0 - for s in self.sections: + for s in list(self.sections.values()): for i in range(s.n3d()): h.pt3dchange(i, s.x3d(i) + dx, s.y3d(i) + dy, s.z3d(i) + dz, s.diam3d(i), sec=s) @@ -335,7 +308,7 @@ def insert_dipole(self, yscale): # dends must have already been created!! # it's easier to use wholetree here, this includes soma sec_list = h.SectionList() - sec_list.wholetree(sec=self.soma) + sec_list.wholetree(sec=self.sections['soma']) sec_list = [sec for sec in sec_list] for sect in sec_list: sect.insert('dipole') @@ -392,7 +365,7 @@ def create_tonic_bias(self, amplitude, t0, T, loc=0.5): loc : float (0 to 1) The location of the input in the soma section. """ - stim = h.IClamp(self.soma(loc)) + stim = h.IClamp(self.sections['soma'](loc)) stim.delay = t0 stim.dur = T - t0 stim.amp = amplitude @@ -409,7 +382,7 @@ def record_soma(self, record_vsoma=False, record_isoma=False): Option to record somatic currents from cells """ - # a soma exists at self.soma + # a soma exists at self.sections['soma'] if record_isoma: # assumes that self.synapses is a dict that exists list_syn_soma = [key for key in self.synapses.keys() @@ -422,7 +395,7 @@ def record_soma(self, record_vsoma=False, record_isoma=False): self.rec_i[key].record(self.synapses[key]._ref_i) if record_vsoma: - self.rec_v.record(self.soma(0.5)._ref_v) + self.rec_v.record(self.sections['soma'](0.5)._ref_v) def syn_create(self, secloc, e, tau1, tau2): """Create an h.Exp2Syn synapse. @@ -460,7 +433,8 @@ def setup_source_netcon(self, threshold): threshold : float The voltage threshold for action potential. """ - nc = h.NetCon(self.soma(0.5)._ref_v, None, sec=self.soma) + nc = h.NetCon(self.sections['soma'](0.5)._ref_v, None, + sec=self.sections['soma']) nc.threshold = threshold return nc diff --git a/hnn_core/network_builder.py b/hnn_core/network_builder.py index 2ac11887d..11ba3a7aa 100644 --- a/hnn_core/network_builder.py +++ b/hnn_core/network_builder.py @@ -503,7 +503,7 @@ def state_init(self): for cell in self.cells: seclist = h.SectionList() - seclist.wholetree(sec=cell.soma) + seclist.wholetree(sec=cell.sections['soma']) for sect in seclist: for seg in sect: if cell.name == 'L2Pyr': From 5d38fecef0d4aeb728630220a02a8662fd381acb Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 16:14:22 -0400 Subject: [PATCH 47/49] TST: add tests for cell class --- hnn_core/cell.py | 6 +++--- hnn_core/tests/test_cell.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index 22a94792e..ff844c88d 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -88,7 +88,7 @@ class Cell: pos : list of length 3 The position of the cell. sections : dict - The sections. The key is the name of the dendrite + The sections. The key is the name of the section and the value is an instance of h.Section. synapses : dict The synapses that the cell can use for connections. @@ -190,8 +190,6 @@ def _create_sections(self, p_secs, topology): for height and xz plane for depth. This is opposite for model as a whole, but convention is followed in this function ease use of gui. """ - if 'soma' not in p_secs: - raise KeyError('soma must be defined for cell') for sec_name in p_secs: sec = h.Section(name=f'{self.name}_{sec_name}') self.sections[sec_name] = sec @@ -269,6 +267,8 @@ def build(self, p_secs, p_syn, topology, sect_loc): } } """ + if 'soma' not in p_secs: + raise KeyError('soma must be defined for cell') self._create_sections(p_secs, topology) self._create_synapses(p_secs, p_syn) self._set_biophysics(p_secs) diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index 69901013d..6f257a14f 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -10,6 +10,8 @@ def test_cell(): """Test cells object.""" + load_custom_mechanisms() + pos = (0., 0., 0.) name = 'test' # GID is assigned exactly once for each cell, either at initialisation... @@ -36,6 +38,38 @@ def test_cell(): with pytest.raises(TypeError, match='secloc must be instance of'): cell.syn_create(0.5, e=0., tau1=0.5, tau2=5.) + p_secs = {'blah': 1} + p_syn = {'ampa': dict(e=0, tau1=0.5, tau2=5.)} + topology = None + sect_loc = {'proximal': 'soma'} + with pytest.raises(KeyError, match='soma must be defined'): + cell.build(p_secs, p_syn, topology, sect_loc) + + p_secs = { + 'soma': + { + 'L': 39, + 'diam': 20, + 'cm': 0.85, + 'Ra': 200., + 'sec_pts': [[0, 0, 0], [0, 39., 0]], + 'syns': ['ampa'], + 'mechs': { + 'km': { + 'gbar_km': 60 + }, + 'ca': { + 'gbar_ca': lambda x: 3e-3 * x + } + } + } + } + cell.build(p_secs, p_syn, topology, sect_loc) + assert 'soma' in cell.sections + assert cell.sections['soma'].L == p_secs['soma']['L'] + assert cell.sections['soma'].gbar_km == p_secs[ + 'soma']['mechs']['km']['gbar_km'] + def test_artificial_cell(): """Test artificial cell object.""" From abac3b2197f43ae965f9a0537915b920c728c220 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 16:36:42 -0400 Subject: [PATCH 48/49] TST: some basic tests for default cell type --- hnn_core/tests/test_cell.py | 2 ++ hnn_core/tests/test_cells_default.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 hnn_core/tests/test_cells_default.py diff --git a/hnn_core/tests/test_cell.py b/hnn_core/tests/test_cell.py index 6f257a14f..02a5674eb 100644 --- a/hnn_core/tests/test_cell.py +++ b/hnn_core/tests/test_cell.py @@ -1,4 +1,5 @@ import pytest +import pickle import matplotlib @@ -38,6 +39,7 @@ def test_cell(): with pytest.raises(TypeError, match='secloc must be instance of'): cell.syn_create(0.5, e=0., tau1=0.5, tau2=5.) + pickle.dumps(cell) # check cell object is picklable until built p_secs = {'blah': 1} p_syn = {'ampa': dict(e=0, tau1=0.5, tau2=5.)} topology = None diff --git a/hnn_core/tests/test_cells_default.py b/hnn_core/tests/test_cells_default.py new file mode 100644 index 000000000..29c7c82cb --- /dev/null +++ b/hnn_core/tests/test_cells_default.py @@ -0,0 +1,43 @@ +import pytest + +from neuron import h + +from hnn_core.cells_default import pyramidal, basket +from hnn_core.network_builder import load_custom_mechanisms + + +def test_cells_default(): + """Test default cell objects.""" + load_custom_mechanisms() + + with pytest.raises(ValueError, match='Unknown pyramidal cell type'): + l5p = pyramidal(pos=(0, 0, 0), cell_name='blah') + + l5p = pyramidal(pos=(0, 0, 0), cell_name='L5Pyr') + assert len(l5p.sections) == 9 + assert 'apical_2' in l5p.sections + + # smoke test to check if cell can be used in simulation + h.load_file("stdrun.hoc") + h.tstop = 40 + h.dt = 0.025 + h.celsius = 37 + + vsoma = l5p.rec_v.record(l5p.sections['soma'](0.5)._ref_v) + times = h.Vector().record(h._ref_t) + + stim = h.IClamp(l5p.sections['soma'](0.5)) + stim.delay = 5 + stim.dur = 5. + stim.amp = 2. + + h.finitialize() + h.fcurrent() + h.run() + + times = times.to_python() + vsoma = vsoma.to_python() + assert len(times) == len(vsoma) + + with pytest.raises(ValueError, match='Unknown basket cell type'): + l5p = basket(pos=(0, 0, 0), cell_name='blah') From 1ee4a93ea4e747a35eb38d9731fd3fa99792804a Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 5 May 2021 19:42:11 -0400 Subject: [PATCH 49/49] ENH: address ryan comments + simplification --- hnn_core/cell.py | 6 ++---- hnn_core/params_default.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/hnn_core/cell.py b/hnn_core/cell.py index ff844c88d..2836db21e 100644 --- a/hnn_core/cell.py +++ b/hnn_core/cell.py @@ -224,7 +224,7 @@ def build(self, p_secs, p_syn, topology, sect_loc): Parameters ---------- - p_secs : dict + p_secs : nested dict Dictionary with keys as section name. p_secs[sec_name] is a dictionary with keys L, diam, Ra, cm, syns and mech. @@ -307,9 +307,7 @@ def insert_dipole(self, yscale): # dends must have already been created!! # it's easier to use wholetree here, this includes soma - sec_list = h.SectionList() - sec_list.wholetree(sec=self.sections['soma']) - sec_list = [sec for sec in sec_list] + sec_list = list(self.sections.values()) for sect in sec_list: sect.insert('dipole') # Dipole is defined in dipole_pp.mod diff --git a/hnn_core/params_default.py b/hnn_core/params_default.py index 3d5fbc02c..4779a63dc 100644 --- a/hnn_core/params_default.py +++ b/hnn_core/params_default.py @@ -440,7 +440,6 @@ def _secs_L2Pyr(): def _secs_L5Pyr(): """The geometry of the default sections in L5Pyr Neuron.""" - # Neuron shape based on Jones et al., 2009 sec_pts = { 'soma': [[0, 0, 0], [0, 23, 0]], 'apical_trunk': [[0, 23, 0], [0, 83, 0]],