Spinal Dorsal Horn Network Model (Medlock et al 2022)

 Download zip file   Auto-launch 
Help downloading and running models
To explore spinal dorsal horn (SDH) network function, we developed a computational model of the circuit that is tightly constrained by experimental data. Our model comprises conductance-based model neurons that reproduce the characteristic firing patterns of excitatory and inhibitory spinal neurons. Excitatory spinal neuron subtypes defined by calretinin, somatostatin, delta-opioid receptor, protein kinase C gamma, or vesicular glutamate transporter 3 expression or by transient/central spiking/morphology and inhibitory neuron subtypes defined by parvalbumin or dynorphin expression or by islet morphology were synaptically connected according to available qualitative data. Synaptic weights were adjusted to produce firing in projection neurons, defined by neurokinin-1 expression, matching experimentally measured responses to a range of mechanical stimulus intensities. Input to the circuit was provided by three types of afferents (Aß, Ad, and C-fibres) whose firing rates were also matched to experimental data.
1 . Medlock L, Sekiguchi K, Hong S, Dura-Bernal S, Lytton WW, Prescott SA (2022) Multiscale computer model of the spinal dorsal horn reveals changes in network processing associated with chronic pain The Journal of Neuroscience [PubMed]
Citations  Citation Browser
Model Information (Click on a link to find other models with that property)
Model Type: Realistic Network;
Brain Region(s)/Organism:
Cell Type(s): Spinal cord lamina I neuron; Spinal cord lamina I-III interneuron;
Gap Junctions:
Simulation Environment: NetPyNE; NEURON;
Model Concept(s): Sensory processing; Pain processing;
Implementer(s): Medlock, Laura [laura.medlock at mail.utoronto.ca]; Sekiguchi, Kazutaka [kazutaka.sekiguchi at shionogi.co.jp];
generic neuron modelling class

calling from python calls genrn with t-junction electrophysiology and morphology--
peripheral fiber, drg with soma, central fiber
properties taken from Waxman
from neuron import h
import logging as lgg
import re

class gesec():

    def __init__(self, name='sec', ions={'na': 58, 'k': -92, 'ca': -129, 'cl': -89}):
        self.name  = name
        self.sec   = h.Section(name=name)
        self.mechs = []
        self.pps   = []
        self.ions  = {}
        for ion in ions:
            self.ions[ion] = {'e': ions[ion], 'mechs': []}
        self.insert = self.im = self.insert_mech
        self.ims = self.insert_mechs
        self.gm = self.get_mechs
        self.gs = self.get_sec

    def insert_mechs(self, mechs, *mmechs):
        if isinstance(mechs, str):
            for mech in mmechs:
            for mech in mechs:
                if 'ions' not in mech and 'params' not in mech:
                    self.insert_mech(mech, params = mechs[mech])
                    mech = mechs[mech]
                    if 'ions' not in mech: mech['ions'] = {}
                    if 'params' not in mech: mech['params'] = {}
                    self.insert_mech(mech, mechs[mech]['ions'], mechs[mech]['params'])

    def insert_mech(self, mech, ions = {}, params = {}):
        # TODO ->DONE does sec.insert(mech) insert mech if the mechanism already exists in section?
        # answer from NEURON: it shouldn't
        # if not self.sec.has_membrane(mech):
        #     self.sec.insert(mech)
        islst = isinstance(ions, (list, set, tuple))
        # use list, set, tuple or dictionary.
        for ion in ions:
            if ion not in self.ions:
                if islst: self.ions[ion] = {'e': False, 'mechs': []}
                else: self.ions[ion] = {'e': ions[ion], 'mechs': []}
            if islst: pass
                # post is the Nernst potential.
                post = ions[ion]
                if hasattr(self.sec, 'e%s' %(ion)) & post:
                    pre = getattr(self.sec, 'e%s' %(ion))
                    if (pre != post):
                        lgg.info("Note: replacing Nernst potential %s mV -> %s mV" %(pre, post))
                        setattr(self.sec, 'e%s' %(ion), post)
        for param in params:
            # if there is a function for the parameter, call it
            if not callable(params[param]): setattr(self.sec, '%s_%s' %(param, mech), params[param])
            else: self.fset_mech(mech, param, params[param])
        # add mech to any ionlist
        for ion in self.ions.keys():
            if hasattr(self.sec, "i%s_%s" %(ion, mech)):

    def fset_mech(self, mech, param, func):
        for seg in self.sec:
            val = func(seg.x)
            setattr(seg, '%s_%s' %(param, mech), val)

    def set_props(self, props):
        for prop in props:
            if callable(props[prop]):
                self.fset_prop(self.sec, prop, props[prop])
                setattr(self.sec, '%s' %(prop), props[prop])

    def fset_prop(self, prop, func):
        #set properties of the segment, diam
        for seg in self.sec:
            val = func(seg.x)
            setattr(seg, prop, val)

    def get_mechs(self):
        return self.mechs

    def set_nernsts(self):
        # set Nernst for all ions
        for ion in self.ions:
            post = self.ions[ion]['e']
            if post and hasattr(self.sec, "e%s" %(ion)):
                pre = getattr(self.sec, 'e%s' % (ion))
                if (pre != post):
                    lgg.info("Note: replacing Nernst potential %s mV -> %s mV" % (pre, post))
                    setattr(self.sec, 'e%s' % (ion), post)

    def get_sec(self):
        return self.sec

    def __lt__(self, mechs):

    def __call__(self, item):
        return self.sec(item)

#    def __iter__(self):
#    def __next__(self):
#    def __getitem__(self, item):

class genrn():

    def __init__(self,x=0,y=0,z=0,ID=0,v_init=None,
                 secs  = {'genrn': {}},
                 mechs = {},
                 ions  = {},
                 cons  = ()):
        self.tags = {'all': []}
        # secs -> pointer
        self.secs = {}
        self.gesecs = self.tags['all']
#        self.useions = re.compile("USEION ([A-Za-z0-9]+)")
        self.init_cell(secs, ions)
        self.initialize_mechs('all', mechs, ions)
        self.v_init = v_init

    def return_sec(self, sec):
        if isinstance(sec, type(h.Section())): return sec
        elif isinstance(sec, str): return self.secs[sec].sec
        elif isinstance(sec, type(gesec())): return sec.sec
        raise TypeError

    def return_gesec(self, sec):
        if isinstance(self, type(h.Section())): return self.secs[sec.name]
        elif isinstance(sec, str): return self.secs[sec]
        elif isinstance(sec, type(gesec())): return sec
        raise TypeError

    def init_cell(self, secs, ions):
        for sec in secs:
            self.add_comp(sec, ions, sec[0:3])
            self.set_props(sec = sec, props = secs[sec])

    def add_comp(self, sec, ions, *tags):
        sec_ = gesec(sec, ions)
        # sec_ -> pointer
        self.secs[sec] = (sec_)
        self.__dict__[sec] = sec_.sec
        for tag in tags:
            try: self.tags[tag].append(sec_)
            except: self.tags[tag] = [sec_]

    def set_props(self, sec, props):
        sec = self.return_gesec(sec)

    def tag_set_props(self, tag, props):
        for sec in self.tags[tag]:
            for prop in props:
                setattr(sec.sec, '%s' %(prop), props[prop])

    def fset_prop(self, sec, prop, func):
        sec = self.return_gesec(sec)
        #set properties of the segment, diam
        sec.fset_prop(prop, func)

    def insert_mech(self, sec, mech, ions={}, params={}):
        sec = self.return_gesec(sec)
        sec.insert_mech(mech, ions, params)

    def initialize_mechs(self, tag, mechs, ions):
        for sec in self.tags[tag]:
            for mech in mechs:
                for param in mechs[mech]:
                    setattr(sec.sec, '%s_%s' %(param, mech), mechs[mech][param])
            for ion in ions:
                try: setattr(sec.sec, 'e%s' %(ion), ions[ion])
                except: pass

    def fset_mech(self, sec, mech, param, func):
        sec = self.return_gesec(sec)
        sec.fset_mech(mech, param, func)

    def connect_secs(self, cons):
        for con in cons:
                exestr = 'self.%s.connect(self.%s)' %(con[0], con[1])
                lgg.info('%s[1] -> %s[0]' %(con[1], con[0]))
                lgg.info('failed to connect: %s[1] -> %s[0]' %(con[1], con[0]))

    def edit_mechs(self, tag, mech, param, value):
        for sec in self.tags[tag]:
            setattr(sec.sec, '%s_%s' %(param, mech), value)

    def tag_fedit_mechs(self, tag, mech, param, func):
        for sec in self.tags[tag]:
            for seg in sec.sec:
                val = func(seg.x)
                setattr(seg, '%s_%s' %(param, mech), val)

    def get_dict(self, tag = 'all'):
        rpr = {}
        for sec in self.tags[tag]:
            rpr[sec.sec] = sec.sec.psection()
        return rpr

# additional not called init functions
    def init_nernsts(self):
        for sec in self.secs:

    def init_pas(self, v_init, set_pas = False):
        e = {}
        i_net = 0
        lgg.info("fcurrent() values (%s mV)" %(v_init))
        for sec in self.secs:
            for mech in sec.mechs:
                for ion in sec.ions:
                        i = getattr(sec.sec, 'i%s_%s' %(ion, mech))
                        lgg.info("(%s)->%s:->%s=%s mA/cm2" %(sec.name, mech, ion, i))
                        i_net += i
                    except: pass
            lgg.info("(%s)->i_net = %s" %(sec.name, i_net))
                e_pas = sec.sec.v + i_net / sec.g_pas
                lgg.info("(%s)->e_pas calculated at %s mV" %(e_pas))
                if set_pas:
                    sec.sec.e_pas = e_pas
            except: pass

## OoOP: indexing>function>unary>power>mul>add>bitshift>and>xor>or>gt
    def __truediv__(self, item):
        return self.secs[item]

    def __gt__(self, item):
        #retrieve gesec objects in a tag using '>' operator, by tag or section name (i.e. self>'all')
        try: return self.tags[item]
        except KeyError: return self.secs[item]

    def __rshift__(self, tag):
        #retrieve section objects in a tag using '>>' operator (i.e. self>>'all)
        return [sec.sec for sec in self.tags[tag]]

    def __call__(self, item):
        #returns the gesec items of a specific tag
        try: return self.tags[item]
        except KeyError: return self.secs[item]

    def __getitem__(self, item):
        #indices for sections (sections stored in order of creation)
        return self.tags['all'][item].sec

    def __repr__(self):
        #printing a shows consolidated information about class
        rpr = ''
        for sec in self.tags['all']:
            r = sec.sec.psection()
            rpr += '%s\n' %(sec.sec.name())
            rpr += 'parent:\t%s\n' %(r['morphology']['parent'])
            rpr += 'morphology:\tL:%f\tdiam:%f\n' %(r['morphology']['L'], max(r['morphology']['diam']))
            rpr += 'mechs:\t%s\n\n' %(list(r['density_mechs'].keys()))
        return rpr

def cal_nseg( sec, freq, d_lambda ):
#neuron+python of https://www.neuron.yale.edu/neuron/static/docs/d_lambda/d_lambda.html
    nseq = lambda fc_: int((sec.L / (d_lambda * fc_) + 0.9) / 2) * 2 + 1
    fpfrc = 4 * h.PI * freq * sec.Ra * sec.cm
    fc = 0
    n3d = sec.n3d()
    if n3d < 2:
        fc = 1e5 * h.sqrt(sec.diam / (fpfrc))
        return nseq(fc)

    x1 = sec.arc3d(0)
    d1 = sec.diam3d(0)

    for i in range(n3d):
        x2 = sec.arc3d(i)
        d2 = sec.diam3d(i)
        fc += (x2 - x1) / h.sqrt(d1 + d2)
        x2 = x1
        d2 = d1

    fc *= h.sqrt(2) * 1e-5 * h.sqrt(fpfrc)
    return nseq(sec.L/fc)

# for debugging
if __name__ == '__main__':# section morphologies
    #        sec         dimensions
    # from tjunction paper
    #secs = {'axnperi': {'nseg':100, 'L':5000, 'diam': 0.8, 'cm': 1.2, 'Ra': 123 },
    #        'drgperi': {'nseg':100, 'L':100,  'diam': 0.8, 'cm': 1.2, 'Ra': 123 },
    #        'drgstem': {'nseg':100, 'L':75,   'diam': 1.4, 'cm': 1.2, 'Ra': 123 },
    #        'drgsoma': {'nseg':1,   'L':25,   'diam': 25 , 'cm': 1.2, 'Ra': 123 },
    #        'drgcntr': {'nseg':100, 'L':100,  'diam': 0.4, 'cm': 1.2, 'Ra': 123 },
    #        'axncntr': {'nseg':100, 'L':5000, 'diam': 0.4, 'cm': 1.2, 'Ra': 123 }}

    # our values:
    # nseg with frequency<50, d_lambda 0.1
    # use cal_nseg(sec, 50, 0.1) for values
    # props for the sections
    secs = {'drgperi': {'nseg':257, 'L':5000,  'diam': 0.8, 'cm': 1.2, 'Ra': 123 },
            'drgstem': {'nseg':3  , 'L':75  ,  'diam': 1.4, 'cm': 1.2, 'Ra': 123 },
            'drgsoma': {'nseg':1  , 'L':30  ,  'diam': 23 , 'cm': 1.2, 'Ra': 123 },
            'drgcntr': {'nseg':363, 'L':5000,  'diam': 0.4, 'cm': 1.2, 'Ra': 123 }}

    # section mechanisms
    mechs = {'nav17m' : {'gnabar': 0.018 },
             'nav18m' : {'gnabar': 0.026 },
             'kdr'    : {'gkbar' : 0.0035},
             'ka'     : {'gkbar' : 0.0055},
             'pas'    : {'g': 5.75e-5, 'e': -58.91}}

    # ion reversal potentials
    ions  = {'na':  67.1,
             'k' : -84.7 }

    # connection list

    #                                                            #
    #           What the morphology looks like (paper)           #
    #                            [1]                             #
    #                          drgsoma                           #
    #                            [0]                             #
    #                            [1]                             #
    #                          drgstem                           #
    #                            [0]                             #
    # [0]anxperi[1]-[0]drgperi[1]-^-[0]drgscntr[1]-[0]axncntr[1] #
    #                                                            #
    #              ^                              ^              #
    #              |     axon initial segment     |              #
    #                                                            #
    #            0    ->    1

    #cons = (('drgperi', 'axnperi'),
    #        ('axncntr', 'drgcntr'),
    #        ('drgstem', 'drgperi'),
    #        ('drgsoma', 'drgstem'),
    #        ('drgcntr', 'drgperi'))

    # simplified connection list
    cons = (('drgstem', 'drgperi'),
            ('drgsoma', 'drgstem'),
            ('drgcntr', 'drgperi'))

    args = {'secs': secs, 'mechs': mechs, 'ions': ions, 'cons': cons}
    test = genrn(**args)