// CA1 heteroassociative memory network: Storage and recall
// CA1 PCs, BCs, AACs, BSCs and OLMs (using moderate cell models)
// EC, CA3 (excitatory) and Septal (inhibitory) inputs
// Cycle is: Recall-Storage-Recall etc
// Parallel code adapted from Hines' ran3par.hoc
// VCU & BPG 8-1-09

// Results reported in V. Cutsuridis, S. Cobb and B.P. Graham,
// "Encoding and retrieval in a model of the hippocampal CA1 microcircuit",
// Hippocampus, in press, DOI 10.1002/hipo.20661, 2009.


{load_file("nrngui.hoc")}  // load the GUI and standard run libraries


{load_file("pyramidal_cell_14Vb.hoc")}
{load_file("basket_cell17S.hoc")}
{load_file("axoaxonic_cell17S.hoc")}
{load_file("bistratified_cell13S.hoc")}
{load_file("olm_cell2.hoc")}
{load_file("stim_cell.hoc")}
{load_file("burst_cell.hoc")}

{load_file("ranstream.hoc")}  // to give each cell its own sequence generator

objref pc
pc = new ParallelContext()


STARTDEL = 50	// msecs
THETA = 250	// msecs (4 Hz)
GAMMA = 25	// msecs (40 Hz)
ECCA3DEL = 9	// msecs

SIMDUR = STARTDEL + (THETA*8)	// simulation duration (msecs)


//////////////////////////////////
// Step 1: Define the cell classes
//////////////////////////////////

npcell = 100	
nbcell = 2	
naacell = 1		
nbscell = 1	
nolm = 	1
nCA3 = 100
nEC = 20
nSEP = 10

ncell = npcell+nbcell+naacell+nbscell+nolm	// total number of cells
nstim = nCA3+nEC+nSEP		// total number of inputs
ntot = ncell+nstim

// gid ordering: 
//	PCs:0..npcell-1
//	BCs:npcell..npcell+nbcell-1
//	etc
// indices of first cell of each type in list "cells"
iPC = 0
iBC = npcell
iAAC = npcell+nbcell
iBSC = npcell+nbcell+naacell
iOLM = npcell+nbcell+naacell+nbscell
iCA3 = npcell+nbcell+naacell+nbscell+nolm
iEC = npcell+nbcell+naacell+nbscell+nolm+nCA3
iSEP = npcell+nbcell+naacell+nbscell+nolm+nCA3+nEC


//////////////////////////////////////////////////////////////
// Steps 2 and 3 are to create the cells and connect the cells
//////////////////////////////////////////////////////////////

C_P = 1  // probability of excitatory connections received by each CA1 PC
         // from CA3 inputs (1 gives full connectivity)
         
SPATT = 20	// number of active cells per pattern
NPATT = 5	// number of stored patterns
NSTORE = 5	// number of new patterns to store

CPATT = 1	// index of cue pattern
CFRAC = 1	// fraction of active cells in cue
iPPC=1		// index of a pattern PC (1st patt in 5 patterns)
iNPPC=0		// index of a non-pattern PC (1st patt in 5 patterns)

strdef FCONN, FPATT, FSTORE	// file name of connection weights and patterns
// (cue and EC patterns taken from FSTORE file to implement storage)
// (use same file for FPATT and FSTORE to test recall only)
FCONN = "Weights/wgtsN100S20P5.dat"
FPATT = "Weights/pattsN100S20P5.dat"	// already stored patterns
FSTORE = "Weights/pattsN100S20P5.dat"	// new patterns to store

// Simple connectivity
CA3_PC = nCA3  // # of connections received by each PC from CA3 cells (excit)
CA3_BC = nCA3  // # of connections received by each BC from CA3 cells (excit)
CA3_AAC = nCA3  // # of connections received by each BC from CA3 cells (excit)
CA3_BSC = nCA3  // # of connections received by each BC from CA3 cells (excit)
EC_PC = nEC  // # of connections received by each PC from EC cells (excit)
EC_BC = nEC  // # of connections received by each BC from EC cells (excit)
EC_AAC = nEC  // # of connections received by each AAC from EC cells (excit)

SEP_BC = nSEP  // # of connections received by each basket cell from septum (inhib)
SEP_AAC = nSEP  // # of connections received by each AAC cell from septum (inhib)
SEP_BSC = nSEP  // # of connections received by each BSC cell from septum (inhib)
SEP_OLM = nSEP  // # of connections received by each OLM cell from septum (inhib)

PC_PC = 1  // # of connections received by each PC from other PCs (excit)
PC_BC = npcell  // # of connections received by each basket cell from PCs (excit)
PC_BSC = npcell  // # of connections received by each bistratified cell from PCs (excit)
PC_AAC = npcell  // # of connections received by each bistratified cell from PCs (excit)
PC_OLM = npcell  // # of connections received by each OLM cell from PCs (excit)

BC_PC = 2  // # of connections received by each PC from basket cells (inhib)
BC_BSC = 2  // # of connections received by each BSC from basket cells (inhib)
BC_OLM = 2  // # of connections received by each OLM from basket cells (inhib)
BC_BC = 1  // # of connections received by each BC from other BCs (inhib)
AAC_PC = 1  // # of connections received by each PC from axoaxonic cells (inhib)
BSC_PC = 1  // # of connections received by each PC from bistratified cells (inhib)
BSC_BC = 1  // # of connections received by each BC from bistratified cells (inhib)
OLM_PC = 1  // # of connections received by each PC from OLM cells (inhib)
OLM_BC = 1  // # of connections received by each basket cell from OLM cells (inhib)

Pcell2Pcell_weight = 0.001
Pcell2Pcell_delay = 1

Bcell2Pcell_weight = 0.02
Bcell2Pcell_delay = 1
Pcell2Bcell_weight = 0.0005 
Pcell2Bcell_delay = 1
Bcell2Bcell_weight = 0.001
Bcell2Bcell_delay = 1
Bcell2BScell_weight = 0.02
Bcell2BScell_delay = 1
Bcell2OLMcell_weight = 0.0
Bcell2OLMcell_delay = 1

AAcell2Pcell_weight = 0.04
AAcell2Pcell_delay = 1
Pcell2AAcell_weight = 0.0005
Pcell2AAcell_delay = 1

BScell2Pcell_weight = 0.002	
BScell2Pcell_delay = 1
BScell2Pcell_GABAB_weight = 0.0004
BScell2Pcell_delay = 1
Pcell2BScell_weight = 0.0005
Pcell2BScell_delay = 1
BScell2Bcell_weight = 0.01
BScell2Bcell_delay = 1

OLMcell2Pcell_weight = 0.04
OLMcell2Pcell_GABAB_weight = 0.0004
OLMcell2Pcell_delay = 1
OLMcell2Bcell_weight = 0.01
OLMcell2Bcell_delay = 1
Pcell2OLMcell_weight = 0.00005
Pcell2OLMcell_delay = 1
OLMcell2Bcell_weight = 0.0
OLMcell2Bcell_delay = 1

// Synapse indices
// onto CA1 PCs
E_EC = 0	// EC AMPA excit to medium SLM (2 of)
E_CA3 = 2	// CA3 AMPA excit to medium SR
EN_CA3 = 3	// CA3 NMDA excit to medium SR
EM_CA3 = 23	// CA3 modifiable (STDP) AMPA excit to medium SR
E_PC = 4	// CA1 recurrent AMPA excit to proximal SR
I_BC = 5	// ff&fb inhib via BCs to soma
I_AAC = 6	// ff&fb inhib via AACs to axon initial segment
I_BSC = 11	// ff&fb inhib via BSCs to SR med (12 of: 6 GABAA, 6 GABAB)
I_OLM = 7	// fb inhib via OLMs to SLM (4 of: 2 GABAA, 2 GABAB)

// onto INs (BC, AAC, BSC)
EI_EC = 0	// EC AMPA excit (2 of; not onto BSC)
EI_CA3 = 2	// CA3 AMPA excit (4 of)
EI_PC = 6	// CA1 PC AMPA excit (2 of)
II_SAME = 8	// inhib from neighbouring INs (BC->BC; BSC->BSC)
II_OPP = 9	// inhib from other INs (BSC->BC; BC->BSC)
II_SEP = 10	// inhib from septum (4 of: 2 GABAA, 2 GABAB)

// onto INs (OLM)
EO_PC = 0	// CA1 PC AMPA excit (2 of)
IO_IN = 2	// inhib from INs and septum (2 of: 1 GABAA, 1 GABAB)

// Septal inhibition
SEPNUM = 1000	// number of SEP spikes
SEPSTART = STARTDEL+(THETA/12)	// time of first SEP spike
SEPINT = 20	// SEP spike ISI (during burst)
SEPNOISE = 0.4	// SEP ISI noise
SEPBINT = 2*THETA/3	// SEP interburst interval
SEPBLEN = THETA/3	// SEP burst length
SEPWGT = 0.02	// SEP weight to BCs and AACs
SEPWGTL = 0.0002	// SEP weight to BSCs and OLMs
SEPDEL = 1	// SEP delay

// Background excitation (not used)
ENUM = 0	// number of spikes
ESTART = 0	// time of first spike
EINT = 200	// spike ISI
ENOISE = 1	// ISI noise
EWGT = 0.001	// excitatory weights (AMPA)
ENWGT = 0.002	// excitatory weights (NMDA)
EDEL = 1	// delay (msecs)

// EC excitation
ECPATT = 1	// index of output pattern
ECNUM = 1000	// number of EC spikes
ECSTART = STARTDEL	// time of first EC spike
ECINT = GAMMA	// EC spike ISI
ECNOISE = 0.2	// EC ISI noise
ECWGT = 0.0	// EC weight to PCs
//ECWGT = 0.001	// EC weight to PCs
ECDEL = 1	// EC delay
EIWGT = 0.00015	// excitatory weights to INs
EIDEL = 1	// delay (msecs)

// Cue (CA3) excitation
CNUM = 1000	// number of cue spikes
CSTART = STARTDEL+ECCA3DEL	// time of first cue spike
CINT = GAMMA	// cue spike ISI
CNOISE = 0.2	// cue ISI noise
CHWGT = 0.0015	// cue weight
CLWGT = 0.0005	// unlearnt weight (usually 0)
CNWGT = 0.0005	// excitatory weights (NMDA)
CDEL = 1	// cue delay

// STDP configuration
STDPDFAC = 0	// depression factor
STDPPFAC = 0	// potentiation factor
//STDPDFAC = 0.2	// depression factor
//STDPPFAC = 1.0	// potentiation factor
AMPASUPP = 0.4	// fraction of AMPA during storage
STDPTHRESH = -55	// voltage threshold for STDP
STDPSTART = STARTDEL+(THETA/2)	// STDP starts at same time as EC input
STDPINT = THETA/2	// STDP interburst (recall) interval
STDPLEN = THETA/2	// STDP burst (storage) length


connect_random_low_start_ = 1  // low seed for mcell_ran4_init()

objref cells, nclist, ncslist, ncelist  // cells will be a List that holds 
  // all instances of network cells that exist on this host
  // nclist will hold all NetCon instances that exist on this host
  // and connect network spike sources to targets on this host (nclist)
  // ncslist holds NetConns from input spike sources (NetStims)
objref ranlist  // for RandomStreams on this host

objref stims, stimlist, cuelist, EClist	// phasic and tonic cell stimulation

objref gidvec  // to associate gid and position in cells List
  // useful for setting up connections and reporting connectivity


// Make the network
proc mknet() {

  print "Make cells..."
  mkcells()  // create the cells
  
  print "Make inputs..."
  mkinputs()  // create the CA3, EC and septal inputs
    
  print "Connect cells..."
  
  mcell_ran4_init(connect_random_low_start_)
  nclist = new List()
  
  //print "   EC..."
  // EC to BC
  connectcells(nbcell, iBC, nEC, iEC, EC_BC, EI_EC, EI_EC+1, EIDEL, EIWGT)
  // EC to AAC
  connectcells(naacell, iAAC, nEC, iEC, EC_AAC, EI_EC, EI_EC+1, EIDEL, EIWGT)

  //print "   CA3..."
  // CA3 to BC
  connectcells(nbcell, iBC, nCA3, iCA3, CA3_BC, EI_CA3, EI_CA3+3, EIDEL, EIWGT)
  // CA3 to AAC
  connectcells(naacell, iAAC, nCA3, iCA3, CA3_AAC, EI_CA3, EI_CA3+3, EIDEL, EIWGT)
  // CA3 to BSC
  connectcells(nbscell, iBSC, nCA3, iCA3, CA3_BSC, EI_CA3, EI_CA3+3, EIDEL, EIWGT)

  //print "   septum..."
  // SEP to BC
  connectcells(nbcell, iBC, nSEP, iSEP, SEP_BC, II_SEP, II_SEP+1, SEPDEL, SEPWGT)
  // SEP to AAC
  connectcells(naacell, iAAC, nSEP, iSEP, SEP_AAC, II_SEP, II_SEP+1, SEPDEL, SEPWGT)
  // SEP to BSC
  connectcells(nbcell, iBC, nSEP, iSEP, SEP_BSC, II_SEP, II_SEP+1, SEPDEL, SEPWGTL)
  // SEP to OLM
  connectcells(naacell, iAAC, nSEP, iSEP, SEP_OLM, IO_IN, IO_IN, SEPDEL, SEPWGTL)

  //print "   PC..."
  // PC to PC
  connectcells(npcell, iPC, npcell, iPC, PC_PC, E_PC, E_PC, Pcell2Pcell_delay, Pcell2Pcell_weight)
  // PC to BC
  connectcells(nbcell, iBC, npcell, iPC, PC_BC, EI_PC, EI_PC+1, Pcell2Bcell_delay, Pcell2Bcell_weight)
  // PC to AAC
  connectcells(naacell, iAAC, npcell, iPC, PC_AAC, EI_PC, EI_PC+1, Pcell2AAcell_delay, Pcell2AAcell_weight)
  // PC to BSC
  connectcells(nbscell, iBSC, npcell, iPC, PC_BSC, EI_PC, EI_PC+1, Pcell2BScell_delay, Pcell2BScell_weight)
  // PC to OLM
  connectcells(nolm, iOLM, npcell, iPC, PC_OLM, EO_PC, EO_PC+1, Pcell2OLMcell_delay, Pcell2OLMcell_weight)

  //print "   INs..."
  // BC to PC
  connectcells(npcell, iPC, nbcell, iBC, BC_PC, I_BC, I_BC, Bcell2Pcell_delay, Bcell2Pcell_weight)
  // BC to BC
  connectcells(nbcell, iBC, nbcell, iBC, BC_BC, II_SAME, II_SAME, Bcell2Bcell_delay, Bcell2Bcell_weight)
  // BC to BSC
  connectcells(nbscell, iBSC, nbcell, iBC, BC_BSC, II_OPP, II_OPP, Bcell2BScell_delay, Bcell2BScell_weight)
  // BC to OLM
  //connectcells(nolm, iOLM, nbcell, iBC, BC_OLM, IO_IN, IO_IN, Bcell2OLMcell_delay, Bcell2OLMcell_weight)
  // AAC to PC
  connectcells(npcell, iPC, naacell, iAAC, AAC_PC, I_AAC, I_AAC, AAcell2Pcell_delay, AAcell2Pcell_weight)
  // BSC to PC
  connectcells(npcell, iPC, nbscell, iBSC, BSC_PC, I_BSC, I_BSC+5, BScell2Pcell_delay, BScell2Pcell_weight)
  connectcells(npcell, iPC, nbscell, iBSC, BSC_PC, I_BSC+6, I_BSC+11, BScell2Pcell_delay, BScell2Pcell_GABAB_weight)
  // BSC to BSC
  //connectcells(nbscell, iBSC, nbscell, iBSC, BSC_BSC, II_SAME, II_SAME, BScell2BScell_delay, BScell2BScell_weight)
  // BSC to BC
  connectcells(nbcell, iBC, nbscell, iBSC, BSC_PC, II_OPP, II_OPP, BScell2Bcell_delay, BScell2Bcell_weight)
  // OLM to PC
  connectcells(npcell, iPC, nolm, iOLM, OLM_PC, I_OLM, I_OLM+1, OLMcell2Pcell_delay, OLMcell2Pcell_weight)
  connectcells(npcell, iPC, nolm, iOLM, OLM_PC, I_OLM+2, I_OLM+3, OLMcell2Pcell_delay, OLMcell2Pcell_GABAB_weight)
  // OLM to BC
  //connectcells(nbcell, iBC, nolm, iOLM, OLM_BC, II_OPP, II_OPP, OLMcell2Bcell_delay, OLMcell2Bcell_weight)

  //print "Connect inputs..."
  // EC input to PCs
  connectEC(FPATT, ECPATT, NPATT, E_EC, 2)	// restore existing pattern
  //connectEC(FSTORE, ECPATT, NSTORE, E_EC, 2)	// store new pattern
  // CA3 input to PCs
  //connectCA3($s1, $2, E_CA3, EN_CA3)	// with fixed synapses
  connectCA3($s1, $2, EM_CA3, EN_CA3)	// with modifiable synapses
  
}


// creates the cells and appends them to a List called cells
// argument is the number of cells to be created
proc mkcells() {local i,j  localobj cell, nc, nil
  cells = new List()
  ranlist = new List()
  gidvec = new Vector()
  // each host gets every nhost'th cell,
  // starting from the id of the host
  // and continuing until no more cells are left
  for (i=pc.id; i < ntot; i += pc.nhost) {
    if (i < iBC) {
      cell = new PyramidalCell()
    } else if (i < iAAC) {
      cell = new BasketCell()	// BC
    } else if (i < iBSC) {
      cell = new AACell()	// AAC
    } else if (i < iOLM) {
      cell = new BistratifiedCell()	// BSC
    } else if (i < iCA3) {
      cell = new OLMCell()	// OLM
    } else if (i < iEC) {
      cell = new StimCell()	// CA3 input
    } else if (i < iSEP) {
      cell = new StimCell()	// EC input
    } else {
      cell = new BurstCell()	// Septal input
    }
    cells.append(cell)
    pc.set_gid2node(i, pc.id)  // associate gid i with this host
    nc = cell.connect2target(nil)  // attach spike detector to cell
    pc.cell(i, nc)  // associate gid i with spike detector
    ranlist.append(new RandomStream(i))  // ranlist.o(i) corresponds to
    gidvec.append(i)
  }
}


// reports distribution of cells across hosts
proc report_gidvecs() { local i, rank
  pc.barrier()  // wait for all hosts to get to this point
  if (pc.id==0) printf("\ngidvecs on the various hosts\n")
  for rank=0, pc.nhost-1 {  // host 0 first, then 1, 2, etc.
    if (rank==pc.id) {
      print "host ", pc.id
      gidvec.printf()
    }
    pc.barrier()  // wait for all hosts to get to this point
  }
}


// sets the CA3, EC and Septal background inputs
proc mkinputs() {local i localobj stim, rs
  for i=0, cells.count-1 {
    gid = gidvec.x[i]	// id of cell
    if (gid >= iCA3 && gid < ntot-nSEP-nEC) {	// appropriate target cell
    // set background activity for excitatory inputs
    stim = cells.object(i).stim
    stim.number = ENUM
    stim.start = ESTART
    stim.interval = EINT
    stim.noise = ENOISE
    }
    if (gid >= iEC && gid < ntot-nSEP) {	// appropriate target cell
    // set background activity for excitatory inputs
    stim = cells.object(i).stim
    stim.number = ENUM
    stim.start = ESTART
    stim.interval = EINT
    stim.noise = ENOISE
    }
    if (gid >= iSEP && gid < ntot) {	// appropriate target cell
    // set background activity for septum
    stim = cells.object(i).stim
    rs = ranlist.object(i)
    stim.number = SEPNUM
    stim.start = SEPSTART
    stim.interval = SEPINT
    stim.noise = SEPNOISE
    stim.burstint = SEPBINT
    stim.burstlen = SEPBLEN
    // Use the gid-specific random generator so random streams are
    // independent of where and how many stims there are.
    stim.noiseFromRandom(rs.r)
    rs.r.negexp(1)
    rs.start()
    }
  }
}


// Target cells receive "convergence" number of inputs from
// the pool of source cells (only one input per source cell at most)
// ("convergence" not reached if no. of sources < convergence)
// connectcells(number of targets, first target cell, 
//		number of source cells, first source cell, 
//		convergence, first synapse,
//		last synapse, connection delay, weight)
// appends the NetCons to a List called nclist
proc connectcells() {local i, j, gid, nsyn, r  localobj syn, nc, rs, u
  // initialize the pseudorandom number generator
  u = new Vector($3)  // for sampling without replacement
  for i=0, cells.count-1 {	// loop over possible target cells
    gid = gidvec.x[i]	// id of cell
    if (gid >= $2 && gid < $1+$2) {	// appropriate target cell
      rs = ranlist.object(i)  // RandomStream for cells.object(i)
      rs.start()
      rs.r.discunif($4, $4+$3-1)  // return source cell index
      u.fill(0)  // u.x[i]==1 means spike source i has already been chosen
      nsyn = 0
      while (nsyn < $5 && nsyn < $3) {
        r = rs.repick()
        // no self-connection and only one connection from any source
        if (r != gidvec.x[i]) if (u.x[r-$4] == 0) {
          // target synapses
          for j = $6, $7 {
            // set up connection from source to target
            syn = cells.object(i).pre_list.object(j)
            nc = pc.gid_connect(r, syn)
            nclist.append(nc)
            nc.delay = $8
            nc.weight = $9
          }
          u.x[r-$4] = 1
          nsyn += 1
        }
      }
    }
  }
}


// connects the EC input layer to PC cells
// read active PCs from pattern file
// all-to-all connectivity between EC and PC pattern cells
// appends the PC NetCons to a List called ncslist
proc connectEC() {local i, gid, ncue  localobj cue, cstim, syn, src, nc, fp, target
  ncelist = new List()
  // open patterns file
  fp = new File($s1)
  fp.ropen()
  cue = new Vector(npcell)
  cue.scanf(fp, $2, $3)	// read pattern
  fp.close()
  ncue = 0
  // find active cells in pattern
  for i=0, cue.size()-1 {
    if (!pc.gid_exists(i+iPC)) { continue }
    if (cue.x[i] == 1) {
      // print "Pattern cell ", i
      target = pc.gid2cell(i+iPC)
      // target synapses
      for k = $4, $4+$5-1 {
        syn = target.pre_list.object(k)	// excitatory synapse
        // create pattern stimulus
        for j=0, nEC-1 {
          // set up connection from source to target
          nc = pc.gid_connect(j+iEC, syn)
          ncelist.append(nc)
          nc.delay = ECDEL
          nc.weight = ECWGT
        }
      }
    }
  }
}


// connects the CA3 input layer to output cells (PCs and INs)
// read PC connections from a file, with connections to
// a target being a column with index i for target cell i
// appends the PC NetCons to a List called ncslist
proc connectCA3() {local i, j, cp, gid  localobj src, syn, synN, nc, fc, rs, conns, rc
  cp = $2	// connection probability
  mcell_ran4_init(connect_random_low_start_)
  conns = new Vector(nCA3)  // connection weights
  rc = new Vector(nCA3)  // random physical connectivity
  ncslist = new List()
  //ncilist = new List()
  
  // inputs to PCs determined by weight matrix
  for i=0, cells.count-1 {	// loop over possible target cells
    gid = gidvec.x[i]	// id of cell
    if (gid >= iPC && gid < npcell+iPC) {	// appropriate target cell
    print "cell ", gid
    syn = cells.object(i).pre_list.object($3)	// AMPA synapse with STDP
    syn.wmax = CHWGT
    syn.wmin = CLWGT
    syn.d = STDPDFAC	// depression
    syn.p = STDPPFAC	// potentiation
    syn.gscale = AMPASUPP	// fraction of AMPA during storage
    syn.thresh = STDPTHRESH	// threshold for postsynaptic voltage detection
    syn.gbdel = STDPSTART
    syn.gbint = STDPINT
    syn.gblen = STDPLEN
    synN = cells.object(i).pre_list.object($4)	// NMDA synapse
    rs = ranlist.object(i)  // the corresponding RandomStream
    rs.start()
    rs.r.uniform(0, 1)  // return integer in range 0..1
    rc.setrand(rs.r)	// generate random connectivity
    // open connections file
    fc = new File($s1)
    fc.ropen()
    conns.scanf(fc, gid+1, nCA3)	// read incoming weights for cell gid
    fc.close()
    for j=0, nCA3-1 {
      // only connection if physical connection exists
      if (rc.x[j] <= cp) {
        //print "   src ", j
        // set up connection from source to target NMDA synapse
        nc = pc.gid_connect(j+iCA3, synN)
        ncslist.append(nc)
        nc.delay = CDEL
        nc.weight = CNWGT	// NMDA weight same for all connections
        // high AMPA if weight is 1
        if (conns.x[j] == 1) {
          // set up connection from source to target
          nc = pc.gid_connect(j+iCA3, syn)
          ncslist.append(nc)
          nc.delay = CDEL
          nc.weight = CHWGT
        } else {
          // set up connection from source to target
          nc = pc.gid_connect(j+iCA3, syn)
          ncslist.append(nc)
          nc.delay = CDEL
          nc.weight = CLWGT	// unlearned weight
        }
      }
    }
    }
  }
}


mknet(FCONN, C_P)  // go ahead and create the net!



//////////////////////////////////////////////////
// Instrumentation, i.e. stimulation and recording
//////////////////////////////////////////////////

// setup activity in EC stims
proc mkEC() {local i, necs localobj cstim, rs
  EClist = new Vector()
  necs = 0
  for i=0, cells.count-1 {
    gid = gidvec.x[i]	// id of cell
    if (gid >= iEC && gid < iEC+nEC) {	// appropriate target cell
        // create cue stimulus
        cstim = cells.object(i).stim
    	rs = ranlist.object(i)
        cstim.number = ECNUM
        cstim.start = ECSTART
        cstim.interval = ECINT
        cstim.noise = ECNOISE
        // Use the gid-specific random generator so random streams are
        // independent of where and how many stims there are.
        cstim.noiseFromRandom(rs.r)
        rs.r.normal(0, 1)
        rs.start()
        EClist.append(i)
        necs += 1
    }
  }
}


objref cue, fp

// setup activity pattern in input cue stims
proc mkcue() {local i, j, ncue localobj cstim, target, rs
  cuelist = new Vector()
  // open patterns file
  fp = new File($s1)
  fp.ropen()
  cue = new Vector(nCA3)
  cue.scanf(fp, $2, $4)	// read pattern
//  cue.printf()
  fp.close()
  ncue = 0
  // find active cells in pattern
  for i=0, cue.size()-1 {
    if (!pc.gid_exists(i+iCA3)) { continue }
    if (ncue <= SPATT*$3) { 	// fraction of active cells in cue
      if (cue.x[i] == 1) {
        print "Cue cell ", i
        cstim = pc.gid2cell(i+iCA3)
        for j=0, cells.count-1 {
          if (gidvec.x[j] == i+iCA3) {break}	// find cell index
        }
    	rs = ranlist.object(j)
        // create cue stimulus
        //cstim = target.stim
        cstim.number = CNUM
        cstim.start = CSTART
        cstim.interval = CINT
        cstim.noise = CNOISE
        // Use the gid-specific random generator so random streams are
        // independent of where and how many stims there are.
        cstim.noiseFromRandom(rs.r)
        rs.r.normal(0, 1)
        rs.start()
        cuelist.append(i)
        ncue += 1
      }
    }
  }
//  print "cue size ", ncue
}

// remove activity pattern in input cue stims
proc erasecue() {local i, j localobj cstim
  for i=0, cue.size()-1 {
    if (!pc.gid_exists(i+iCA3)) { continue }
    cstim = pc.gid2cell(i+iCA3)
    cstim.number = 0
  }
}


mkcue(FPATT, CPATT, CFRAC, NPATT)	// cue from already stored pattern
//mkcue(FSTORE, CPATT, CFRAC, NSTORE)	// cue from new pattern
mkEC()



// Spike recording
objref tvec, idvec  // will be Vectors that record all spike times (tvec)
        // and the corresponding id numbers of the cells that spiked (idvec)
proc spikerecord() {local i  localobj nc, nil
  tvec = new Vector()
  idvec = new Vector()
  for i=0, cells.count-1 {
    nc = cells.object(i).connect2target(nil)
    nc.record(tvec, idvec, nc.srcgid)
    // the Vector will continue to record spike times
    // even after the NetCon has been destroyed
  }
}

spikerecord()


// Record cell voltage traces
objref pvsoma, pvsr, pvslm  // Vectors that record voltages from pattern PC
objref npvsoma, npvsr, npvslm  // Vectors that record voltages from non-pattern PC
objref vBC, vAAC, vBSC, vOLM  // Vectors that record voltages from INs

proc vrecord() {local i, gid 
  for i=0, cells.count-1 {	// loop over possible target cells
    gid = gidvec.x[i]	// id of cell
    if (gid==iPPC) {
      pvsoma = new Vector()
      pvsr = new Vector()
      pvslm = new Vector()
      pvsoma.record(&cells.object(i).soma.v(0.5))
      pvsr.record(&cells.object(i).radTmed.v(0.5))
      pvslm.record(&cells.object(i).lm_thick1.v(0.5))
    }
    if (gid==iNPPC) {
      npvsoma = new Vector()
      npvsr = new Vector()
      npvslm = new Vector()
      npvsoma.record(&cells.object(i).soma.v(0.5))
      npvsr.record(&cells.object(i).radTmed.v(0.5))
      npvslm.record(&cells.object(i).lm_thick1.v(0.5))
    }
    if (gid==iBC) {
      vBC = new Vector()
      vBC.record(&cells.object(i).soma.v(0.5))
    }
    if (gid==iAAC) {
      vAAC = new Vector()
      vAAC.record(&cells.object(i).soma.v(0.5))
    }
    if (gid==iBSC) {
      vBSC = new Vector()
      vBSC.record(&cells.object(i).soma.v(0.5))
    }
    if (gid==iOLM) {
      vOLM = new Vector()
      vOLM.record(&cells.object(i).soma.v(0.5))
    }
  }
}

vrecord()


////////////////////////////
// Simulation control
////////////////////////////

strdef fstem
fstem = "Results/HAM_P5R1"

tstop = SIMDUR
celsius = 34

// Do single parallel run
proc prun() {
  pc.barrier()  // wait for all hosts to get to this point
  {pc.set_maxstep(1)}
  stdinit()
  {pc.psolve(tstop)}
  spikeout()
  vout()
}


// Do runs for each stored pattern
proc bpattrun() { local i
  erasecue()
  for i=1, NPATT {
    if (pc.id==0) {print "Cue pattern ", i} // print header once
    sprint(fstem, "Results/HAM_P5R%", i)
    {mkcue(FPATT, i, CFRAC, NPATT)}	// cue from stored pattern
    pc.barrier()  // wait for all hosts to get to this point
    //{pc.set_maxstep(10)}
    {pc.set_maxstep(1)}
    stdinit()
    {pc.psolve(tstop)}
    spikeout()
    erasecue()
  }  
}


////////////////////////////
// Report simulation results
////////////////////////////

objref fo
strdef fno

proc spikeout() { local i  
  pc.barrier()  // wait for all hosts to get to this point
  if (pc.id==0) printf("\ntime\t cell\n")  // print header once
  for rank=0, pc.nhost-1 {  // host 0 first, then 1, 2, etc.
    if (rank==pc.id) {
      sprint(fno,"%s_spt.dat", fstem)
      fo = new File(fno)
      //fo.wopen()
      fo.aopen()
      for i=0, tvec.size-1 {
        printf("%g\t %d\n", tvec.x[i], idvec.x[i])
        fo.printf("%g\t %d\n", tvec.x[i], idvec.x[i])
      }
      fo.close()
    }
    pc.barrier()  // wait for all hosts to get to this point
  }
}


proc vout() { local i, j, gid
  
  for j=0, cells.count-1 {	// loop over possible target cells
    gid = gidvec.x[j]	// id of cell
    if (gid==iPPC) {
      sprint(fno,"%s_pvsoma.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, pvsoma.size-1 {
        fo.printf("%g\n", pvsoma.x[i])
      }
      fo.close()
      sprint(fno,"%s_pvsr.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, pvsr.size-1 {
        fo.printf("%g\n", pvsr.x[i])
      }
      fo.close()
      sprint(fno,"%s_pvslm.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, pvslm.size-1 {
        fo.printf("%g\n", pvslm.x[i])
      }
      fo.close()
    }
    if (gid==iNPPC) {
      sprint(fno,"%s_npvsoma.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, npvsoma.size-1 {
        fo.printf("%g\n", npvsoma.x[i])
      }
      fo.close()
      sprint(fno,"%s_npvsr.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, npvsr.size-1 {
        fo.printf("%g\n", npvsr.x[i])
      }
      fo.close()
      sprint(fno,"%s_npvslm.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, npvslm.size-1 {
        fo.printf("%g\n", npvslm.x[i])
      }
      fo.close()
    }
    if (gid==iBC) {
      sprint(fno,"%s_BC.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, vBC.size-1 {
        fo.printf("%g\n", vBC.x[i])
      }
      fo.close()
    }
    if (gid==iAAC) {
      sprint(fno,"%s_AAC.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, vAAC.size-1 {
        fo.printf("%g\n", vAAC.x[i])
      }
      fo.close()
    }
    if (gid==iBSC) {
      sprint(fno,"%s_BSC.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, vBSC.size-1 {
        fo.printf("%g\n", vBSC.x[i])
      }
      fo.close()
    }
    if (gid==iOLM) {
      sprint(fno,"%s_OLM.dat", fstem)
      fo = new File(fno)
      fo.wopen()
      for i=0, vOLM.size-1 {
        fo.printf("%g\n", vOLM.x[i])
      }
      fo.close()
    }
  }
}


prun()
//bpattrun()
{pc.runworker()}
{pc.done()}
quit()