// $Id: initbatpar.hoc,v 1.7 2011/06/10 01:46:17 ted Exp $

// Batch implementation suitable for serial or parallel execution
// for stimulus current i iterating over a range of values
//   run a simulation and report spike frequency
// save i & corresponding f to a file
// optionally plot fi curve

// wrapping code in { } prevents printing undesired 0s etc. to terminal
{ load_file("stdgui.hoc") } // load standard library but don't open NMM toolbar

objref pc // create ParallelContext instance here
pc = new ParallelContext() // so it exists whenever we need it

///// Simulation parameters

TSTOP = 500 // ms, more than long enough for 15 spikes at ISI = 25 ms
AMP0 = 0.1 // nA--minimum stimulus
D_AMP = 0.02 // nA--stim increment between runs
NRUNS = 30

///// Model specification

{ load_file("cell.hoc") }

///// Instrumentation

// experimental manipulations

objref stim
soma stim = new IClamp(0.5)
stim.del = 1 // ms
stim.dur = 1e9
stim.amp = 0.1 // nA

// $1 is run #
// returns corresponding stimulus amplitude

func fstimamp() {
  return AMP0 + $1*D_AMP
}

// sets stimulus amplitude to appropriate value for this run

proc setparams() {
  stim.amp = fstimamp($1)
}

// data recording and analysis

objref nc, spvec, nil // to record spike times
// count only those spikes that get to distal end of dend
dend nc = new NetCon(&v(1), nil)
nc.threshold = -10 // mV
spvec = new Vector()
{ nc.record(spvec) }

NSETTLE = 5 // ignore the first NSETTLE ISI (allow freq to stablize)
NINVL = 10 // # ISI from which frequency will be calculated
NMIN = NSETTLE + NINVL // ignore recordings with fewer than this # of ISIs

freq = 0

proc postproc() { local nspikes, t1, t2
  freq = 0
  nspikes = spvec.size()
  if (nspikes > NMIN) {
    t2 = spvec.x(nspikes-1) // last spike
    t1 = spvec.x(nspikes-1-NINVL) // NINVL prior to last spike
    freq = NINVL*1e3/(t2-t1) // t1 and t2 are separated by NINVL ISIs
  }
}

///// Simulation control

tstop = TSTOP

func fi() { // set params, execute a simulation, analyze and return results
  setparams($1) // set parameters for this run
  run()
  postproc() // analyze the data
  return freq
}

// batch control

trun = startsw() 

{ pc.runworker() } // start execute loop on the workers
// code beyond this point is executed only by the master
// the master must now post jobs to the bulletin board

objref svec, fvec

proc batchrun() { local ii, tmp
  svec = new Vector()
  fvec = new Vector()
  for ii = 0, $1-1 pc.submit("fi", ii) // post all jobs to bulletin board
  // retrieve results from bulletin board
  while (pc.working) { // if a result is ready, get it; if not, pick a job and do it
    fvec.append(pc.retval()) // get frequency
    pc.unpack(&tmp)
    svec.append(tmp) // get job number
    printf(".") // indicate progress
  }
}

batchrun(NRUNS)

{ pc.done() }

// all simulations have been completed
// and the workers have been released
// but the boss still has things to do

///// Reporting of results

fvec = fvec.index(svec.sortindex()) // rearrange fvec according to svec sortindex
{
svec.sort()
// but svec contains job numbers, not actual stimulus amplitudes
svec.apply("fstimamp") // convert job numbers to stimulus amplitudes
}

// now svec holds the current amplitudes in increasing order
// and fvec holds the corresponding firing frequencies

proc saveresults() { local ii  localobj file
  file = new File($s1)
  file.wopen()
  file.printf("label:%s\n%d\n", "f", fvec.size)
  for ii=0, fvec.size-1 {
    file.printf("%g\t%g\n", svec.x[ii], fvec.x[ii])
  }
  file.close()
}

saveresults("fi.dat")

print " "
print startsw() - trun, "seconds" // report run time

quit()
