Olfactory bulb microcircuits model with dual-layer inhibition (Gilra & Bhalla 2015)

 Download zip file 
Help downloading and running models
Accession:153574
A detailed network model of the dual-layer dendro-dendritic inhibitory microcircuits in the rat olfactory bulb comprising compartmental mitral, granule and PG cells developed by Aditya Gilra, Upinder S. Bhalla (2015). All cell morphologies and network connections are in NeuroML v1.8.0. PG and granule cell channels and synapses are also in NeuroML v1.8.0. Mitral cell channels and synapses are in native python.
Reference:
1 . Gilra A, Bhalla US (2015) Bulbar microcircuit model predicts connectivity and roles of interneurons in odor coding. PLoS One 10:e0098045 [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: Olfactory bulb;
Cell Type(s): Olfactory bulb main mitral GLU cell; Olfactory bulb main interneuron periglomerular GABA cell; Olfactory bulb main interneuron granule MC GABA cell;
Channel(s): I A; I h; I K,Ca; I Sodium; I Calcium; I Potassium;
Gap Junctions:
Receptor(s): AMPA; NMDA; Gaba;
Gene(s):
Transmitter(s): Gaba; Glutamate;
Simulation Environment: Python; MOOSE/PyMOOSE;
Model Concept(s): Sensory processing; Sensory coding; Markov-type model; Olfaction;
Implementer(s): Bhalla, Upinder S [bhalla at ncbs.res.in]; Gilra, Aditya [aditya_gilra -at- yahoo -period- com];
Search NeuronDB for information about:  Olfactory bulb main mitral GLU cell; Olfactory bulb main interneuron periglomerular GABA cell; Olfactory bulb main interneuron granule MC GABA cell; AMPA; NMDA; Gaba; I A; I h; I K,Ca; I Sodium; I Calcium; I Potassium; Gaba; Glutamate;
#!/usr/bin/env python
# -*- coding: utf-8 -*-

########## You need to run:
## python2.6 IPSPs_PGs_vs_granules.py

import os
import sys
import math
import pickle
import datetime

sys.path.extend(["..","../networks","../generators","../simulations"])

from OBNetwork import *

from stimuliConstants import *
## Set OBNet_file in simset_activinhibition to have 10:1 singles, so that IPSCs are same height
from simset_activinhibition import *
from sim_utils import * # has build_tweaks(), and print_extras_activity()
from data_utils import *

RUNTIME = 3.0#6.0 # s # this must be a float, else takes as time steps

## Ensure that you use 10:1 aggregation of singles, if multiplicity = 'singles'
## so that with SYNS_PER_CLUBBED_SINGLE=10, there are no synchronous large IPSCs/IPSPs.
## But there will still be 10 IPSCs of same height for every single spike!
## For joints, 1:1 but there are 4x directed joints which'll show up say 1 in 4.
multiplicity = 'joints' # 'joints' / 'singles'

VOLTAGE_CLAMP = True

mitralmainidx = 0
## set lateral_mitnum to 1 for 2MITS / 2 for 2GLOMS option set in generate_neuroML.py
## Note that for directed connectivity, mit 3 is used for directed conn to mit 0 in generate_neuroml.py,
## thus mit 2 represents a non-directed conn cell.
## If you want to show asymm inhibition between directed cells, you should use mit 3 below.
lateral_mitnum = 2
mitralsidekickidx = lateral_mitnum

from pylab import * # part of matplotlib that depends on numpy but not scipy

## Output file name
today = datetime.date.today()
if NO_SINGLES: singles_str = '_NOSINGLES'
else: singles_str = '_SINGLES'
if NO_JOINTS: joints_str = '_NOJOINTS'
else: joints_str = '_JOINTS'
if NO_PGS: pgs_str = '_NOPGS'
else: pgs_str = '_PGS'
if IN_VIVO: invivo_str = '_invivo'
else: invivo_str = ''
if DIRECTED: dirt_str = '_directed'+str(FRAC_DIRECTED)
else: dirt_str = ''
if REVERSED_ADI: rev_str = '_reversed'
else: rev_str = ''
if ASYM_TEST: asym_str = '_asym'
else: asym_str = ''
if VOLTAGE_CLAMP: vstr = '_vclamp'
else: vstr = ''
#now =  datetime.datetime.now().strftime("%Y_%m_%d_%H_%M")+'_'
now = '' # stable enough to not bother about the date-time of simulation
outfilename = '../results/ADI/'+now+'IPSPs_PGvsgran_seed'+netseedstr+mitdistancestr+\
    singles_str+joints_str+pgs_str+invivo_str+dirt_str+rev_str+asym_str+vstr+'.pickle'
 
#-----------------------------------------------------------

def setup_stim(network):
    num_spikes = int(10*RUNTIME)
    if VOLTAGE_CLAMP:
        mitcell = network.mitralTable[mitralmainidx]
        mitsoma = moose.Compartment(get_matching_children(mitcell, ['Soma','soma'])[0])
        #### I block Ca and K channels in the cell
        #### We don't want these channels to be active when cell is held at 0mV; Na inactivates.
        blockChannels(mitcell, ['K','Ca']) # in moose_utils.py
        ## PID gain: I think for Davison 4-comp-mitral/granule: 0.5e-5 # optimal gain
        ## too high 0.5e-4 drives it to oscillate at high frequency,
        ## too low 0.5e-6 makes it have an initial overshoot (due to Na channels?)
        ## But for BBmit1993, gain of 1e-6 is optimal
        ## from moose.utils, setup_vclamp(compartment, name, delay1, width1, level1, gain=0.5e-5)
        network.PIDITable = setup_vclamp(mitsoma, '_somavclamp', 0, RUNTIME, 0e-3, gain=1e-6)
    ## network.populationDict = 
    ##      { 'populationname1':(cellname,{instanceid1:moosecell, ... }) , ... }
    ## network.projectionDict = 
    ##      { 'projectionname1':(source,target,[(syn_name1,pre_seg_path,post_seg_path),...]) , ... }
    ## PGs are aggregated 5:1 with async synapses. So for every PG spike, there will be 5 IPSCs/IPSPs.
    if 'PGs' in network.populationDict:
        PGs = network.populationDict['PGs'][1]
        PG_conns = network.projectionDict['PG_mitral'][2]
        num_PG_conns = len(PG_conns)
        i = 0
        while i<num_spikes/PG_CLUB:
            ## find a PG-|mitral synapse on the main mitral, and make only that PG spike
            conn_id = int(uniform(num_PG_conns))
            conn = PG_conns[conn_id]
            ## get the mitnum from the post_seg_path
            mitname = string.split(conn[2],'/')[1] # name of the mitral cell from '/mitrals_2/...'
            mitnum = int(string.split(mitname,'_')[1]) # mit number from 'mitrals_2'
            if mitnum != mitralmainidx: continue
            
            ## get the PGnum from the post_seg_path
            PGname = string.split(conn[1],'/')[1] # name of the PG cell from '/PGs_2/...'
            PGnum = int(string.split(PGname,'_')[1]) # PG number from 'PGs_2'
            PG = PGs[PGnum]
            PGsoma = moose.Compartment(get_matching_children(PG, ['Soma','soma'])[0])
            ## We want a single spike at given time, so cannot use soma.inject
            setup_iclamp(PGsoma, '_PG'+str(PGnum),\
                uniform(0,RUNTIME), 1e-3, 200e-12) # start_time, duration, current (all SI)
            i += 1
    ## Ensure that you use 10:1 aggregation of singles, if multiplicity = 'singles'
    ## so that with SYNS_PER_CLUBBED_SINGLE=10, there are no synchronous large IPSCs/IPSPs.
    ## But there will still be 10 IPSCs of same height for every single spike!
    ## For joints, 1:1 but there are 4x directed joints which'll show up say 1 in 4.
    if 'granules_'+multiplicity in network.populationDict:
        grans = network.populationDict['granules_'+multiplicity][1]
        gran_conns = network.projectionDict['granule_mitral_inh_'+multiplicity][2]
        num_gran_conns = len(gran_conns)
        i = 0
        if multiplicity=='singles': club_factor = SYNS_PER_CLUBBED_SINGLE
        else: club_factor = GRANS_CLUB_JOINTS_2GLOMS
        while i<num_spikes/club_factor:
            conn_id = int(uniform(num_gran_conns))
            conn = gran_conns[conn_id]
            ## get the mitnum from the post_seg_path
            mitname = string.split(conn[2],'/')[1] # name of the mitral cell from '/mitrals_2/...'
            mitnum = int(string.split(mitname,'_')[1]) # mit number from 'mitrals_2'
            if mitnum != mitralmainidx: continue
            
            ## get the grannum from the post_seg_path
            granname = string.split(conn[1],'/')[1] # name of the granule cell from '/granules_joints_2/...'
            grannum = int(string.split(granname,'_')[2]) # granule number from 'granules_joints_2'
            gran = grans[grannum]
            gransoma = moose.Compartment(get_matching_children(gran, ['Soma','soma'])[0])
            ## We want a single spike at given time, so cannot use soma.inject
            setup_iclamp(gransoma, '_gran_'+multiplicity+str(grannum),\
                uniform(0,RUNTIME), 1e-3, 100e-9) # start_time, duration, current (all SI)
            i += 1

def run_inhibition(network, tables):
    resetSim(network.context, SIMDT, PLOTDT) # from moose_utils.py sets clocks and resets
    network.context.step(RUNTIME)

def save_and_plot(network):
    if VOLTAGE_CLAMP:
        Vclamp_I = array(network.PIDITable)
    mit_Vm = array(network.mitralTable[mitralmainidx]._vmTableSoma)
    ## sometimes mitVm has one or two extra value
    ## so generate extra time values and discard if mitVm is not that long
    tlist = arange(0.0,RUNTIME+3*PLOTDT,PLOTDT)
    tlist = tlist[:len(Vclamp_I)]

    fvsifile = open(outfilename,'w')
    pickle.dump((tlist,Vclamp_I), fvsifile)
    fvsifile.close()
    print "Wrote",outfilename

    mainfig = figure(facecolor='w')
    mainaxes = mainfig.add_subplot(111)
    if VOLTAGE_CLAMP:
        starti = int(50e-3/PLOTDT)
        mainaxes.plot(tlist[starti:],Vclamp_I[starti:]*1e12,color='k')
        axes_labels(mainaxes,'time (s)','I (pA)')

    mainfig = figure(facecolor='w')
    mainaxes = mainfig.add_subplot(111)
    mainaxes.plot(tlist,mit_Vm*1e3,color='k')
    axes_labels(mainaxes,'time (s)','V (mV)')

    show()

#----------------------------------------------------

if __name__ == "__main__":
    seed([100.0])
    if os.path.exists(outfilename):
        print "Have to implement purely plotting if output file exists:", outfilename
        #sys.exit()
    uniquestr = 'pgvsgran_'
    ## includeProjections gets used only if ONLY_TWO_MITS is True:
    ## Keep below projections to 'second order cells'
    ## i.e. to cells (granules) connected to mits0&1.
    ## The connections between second order cell
    ## and mits0&1 are automatically retained of course.
    ## no need for 'PG' below as 'ORN_PG' and 'SA_PG' are not needed,
    ## and 'PG_mitral', 'mitral_PG' connections to/from mits0&1 are kept automatically.
    includeProjections = ['granule_baseline']
    tweaks = build_tweaks( CLUB_MITRALS, NO_SPINE_INH,\
        NO_SINGLES, NO_JOINTS, NO_MULTIS, NO_PGS, ONLY_TWO_MITS,\
        includeProjections, (mitralmainidx,mitralsidekickidx) )
    ## send mpirank to put in ORN filenames / gran baseline temp files
    ## so they do not clash between mpi processes
    ## also, unique str, so that temp files of morphs, pulses, etc do not overlap
    ## spiketable is False that mitral Vm-s are recorded not spiketimes.
    network = OBNetwork(OBNet_file, synchan_activation_correction,\
        tweaks, mpirank, uniquestr, granfilebase, spiketable=False)
    #printNetTree() # from moose_utils.py

    setup_stim(network)
    ## if SPIKETABLE: record the Vm-s of a few interneurons
    ## else: record spiketimes of all interneurons
    tables = setupTables(network, NO_PGS, NO_SINGLES, NO_JOINTS, NO_MULTIS,
        args={'mitrals':(mitralmainidx,mitralsidekickidx)}, spikes=SPIKETABLE)
    run_inhibition(network, tables)
    save_and_plot(network)