/
synresp.hoc
1281 lines (1070 loc) · 37.6 KB
/
synresp.hoc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// For compatibility with MS Notepad, set tabs to every 8 char.
// Set major run control parameters (0=False=No, 1=True=Yes)
isCC=0 // set CC mode (otherwise VC)
isInteractive=1 // run interactive vs in batch
runStim=0 // run the synaptic stimulation simulation
// Load the Neuron machinery
if (isInteractive) {
load_file("nrngui.hoc")
} else {
load_file("stdrun.hoc")
}
cvode.active(1) // use an adaptive method
// The following are set before this hoc file is invoked.
// Values shown are examples only.
// Define an orientation vector for locating layers.
// These component must be unit length and have
// point in the apical direction of the reoriented
// y coordinate for the cell.
// orientX=0 // No real orientation
// orientY=1 // so the unit vector is
// orientZ=0 // just the y axis direction.
// Locate the (reoriented) boundaries between layers
// PPy3d=350 // Minimum y-coord value for PP (PP-SR boundary)
// SRy3d=100 // Minimum Y-coord value for SR (SR-SL boundary)
// SOy3d=0 // Maximum y-coord value for SO
// Define input and output files (examples only)
// strdef cell // hoc file with cell geometry
// strdef outPath // Synaptic responses file name
// strdef avgPath // Overall average response file name
// cell="bar-cell1zr.CNG.hoc" // Cell geometry file
// outPath="out-temp.txt" // Synaptic responses output file
// Provide the path for the common axon segment definition
strdef axonPath
axonPath="axon-common.hoc"
// Set a default path for saved response data vectors
strdef savePath
savePath="savedsynresp.csv"
// Simulation parameters -----------------------------
addChannels=0 // Add active channels to the cell
useHemond2008=0 // Use conductances from the Hemond et al., 2008 model.
useSpine=0 // Use a spine for synapses in SR and SO
maxSegLen=10 // Maximum segment length in microns
tError=1000 // Maximum time after init before error declared (see runIt)
tInit=2000 // Initialization time to reach equilibrium
tCont=50 // Continue time (must be greater than time to peak)
tDelta=0.1 // Time step size for response simulation and averaging
randSeed=19720107 // Random number generator seed value
maxSecnum=1000 // maximum section number (see findLayer and onPPtrunk)
useAMPAR=1 // Run for AMPAR synapses (ie AMPAR not blocked)
useNMDAR=0 // Run for NMDAR synapses (ie NMDAR not blocked)
fastAMPAR=0 // use RC AMPAR synapse settings with same kinetics as PP
randomizeSyn=0 // make random variations to synaptic properties
maxDendDiam=5 // Maximum dendrite diameter (otherwise soma)
Vrest = -61 // Starting resting potential everywhere
// Vrest = -61 - 13.6 // Starting resting potential using LJP from Hemond et al.
// Vrest = -71 // Starting resting potential shifted by 10 mV
celsius = 32.0 // Temperature (needed for active channels)
synErev = 0 // Synaptic reversal potential (AMPAR and NMDAR)
lowExtMg = 50e-3 // External [Mg++] (mM) for low-Mg experiments
highExtMg = 1 // External [Mg++] (mM) for normal Mg experiments
VCvm = -80 // Voltage clamp potential at soma
CCvm = -60 // Current clamp holding potential - as per model paper predictions
// CCvm = -80 // Current clamp holding potential - more or less per experiments
// PP synapse parameters for AMPAR responses.
// Generally these are similar to values found in MF terminals
// and SC synapses in CA1 measured near the synapse.
ppAMPARSynTau1=0.4 // PP synaptic rising time constant
ppAMPARSynTau2=4.1 // PP synaptic falling time constant
ppAMPARSynWeight=0.9 // PP synapse gmax in nS
ppAMPARSynTsd=0.0 // PP synapse tau2 log-normal sd param
ppAMPARSynWsd=0.2 // PP synapse weight log-normal sd param
// RC synapse parameters for AMPAR responses.
// These values are similar to values from Williams & Johnston 1991.
// The slower kinetics may result from combined AMPA/KA synapses,
// from reduced neurotransmitter concentration in the cleft,
// or even from multi-quantal release, all of which are hypothetical.
rcAMPARSynTau1=3.3 // RC synaptic rising time constant
rcAMPARSynTau2=3.3 // RC synaptic falling time constant
rcAMPARSynWeight=0.5 // RC synapse gmax in nS
rcAMPARSynTsd=0.0 // RC synapse tau2 log-normal sd param
rcAMPARSynWsd=0.4 // RC synapse weight log-normal sd param
if (fastAMPAR) {
// Set RC AMPAR time constants to match fastest experimental pVC response
// rcAMPARSynTau1=1.3
// rcAMPARSynTau2=5.3
// Set RC AMPAR to match PP AMPAR or other faster AMPAR model
rcAMPARSynTau1=0.4
rcAMPARSynTau2=4.1
}
// PP and RC synapse parameters for NMDAR responses.
// This is pure data fitting. The values are somewhat
// in line with Vicini et al. 1998 for HEK transfection,
// but there should be a larger correction for temperature.
// See also Dalby and Mody 2003 for NR2A in DG (similar).
// See Chen et al., 2001 Mol Pharamcol 59: 212-219 for one
// of the few (or only) experimental studies to consider
// both NMDAR subtypes and temperature effects together.
// Mean NMDAR experimental values from Chen et al. are
// 2.4 ms and 35 ms at 37 deg-C in outside-out patches.
// For the parameters below, 10%-90% rise time is 4.7 ms while
// 90%-10% decay time is 37.9 ms. There is no good reason to
// assume the same gmax for RC and PP since the synapses
// have morphological differences.
ppNMDARSynTau1=5 // PP synaptic rising time constant
ppNMDARSynTau2=16 // PP synaptic falling time constant
ppNMDARSynWeight=0.18 // PP synapse gmax in nS
ppNMDARSynTsd=0.0 // PP synapse tau2 log-normal sd param
ppNMDARSynWsd=0.0 // PP synapse weight log-normal sd param
rcNMDARSynTau1=5 // RC synaptic rising time constant
rcNMDARSynTau2=16 // RC synaptic falling time constant
rcNMDARSynWeight=0.16 // RC synapse gmax in nS
rcNMDARSynTsd=0.0 // RC synapse tau2 log-normal sd param
rcNMDARSynWsd=0.0 // RC synapse weight log-normal sd param
// Mean passive values estimated in Hemond et al. 2009 (Ih paper)
// RaAll is not a sensitive fit in that context but a
// more typical value is needed to fit synaptic responses.
// For justification of different spine factors in PP and RC
// see Megias et al. 2001 for results in CA1 as well as
// Matsuda et al. 2004 plus Ishizuka et al. 1995 for CA3.
// Axon properties are from Hemond et al. 2008
Cm=0.72 // Capacitivity in microF/cm^2 (1.44/2)
Rm=62996 // Membrane resistivity in ohm-cm^2 (31498*2)
RaAll=140 // Axial resistivity in ohm-cm
//RaAll=198 // Axial resistivity in ohm-cm
RaAxon=50 // Axon-only axial resistivity
RaSpine=140 // Spine-only axial resistivity
PPSpineAdj=1.0 // Cm,Rm adjustment for PP spines
RCSpineAdj=2.0 // Cm,Rm adjustment for RC spines
// Parameters for hypothetical K+ channels in spine
ekspine = -90 // K+ reversal for channels in spine (in mV)
// gkspine = 120e-3 // K+ density actived in spine (in S/cm^2)
// gkspine = 55e-3 // K+ density actived in spine (in S/cm^2)
gkspine =0 // K+ density actived in spine (in S/cm^2)
// Provide channel conductances. These are in S/cm^2.
// In the model from Hemond et al., 2008 fig 9, there is a shift
// of voltage sensitivity by 24 mV in most channels.
// These conductance settings go with figure 9d (non-delay case)
gna=22e-3 // Sodium channel conductivity (all but axon)
gnaAxon=110e-3 // Sodium channel conductivity for axon
gkdr=10e-3 // K-dr (delayed rectifier) channel conductivity
gkap=20e-3 // K-A channel conductivity
gc=0.01e-3 // Ca-L,N,T channel conductivity in soma (all the same)
ghd=0.00e-3 // Ih channel conductivity
gKc=0.05e-3 // K-C channel conductivity
gahp=0.0e-3 // K-ahp channel conductivity
gkm=0e-3 // K-M channel conductivity (adapting - soma only in this model)
gkd=0e-3 // Kd channel conductivity (delayed onset - soma only in this model)
sh=24 // Global voltage shift in mV for channels (Hemond et al. only)
gkaSlope=5.2/350 // Distance dependency for K-A (not used for Hemond et al.)
gkaSlopeMaxDist=9999 // Maximum distance for K-A dependency (not used for Hemond et al.)
// Misc working variables (global) ----------------------------------
objref ns // Common network stimulation
objref amparNC, nmdarNC // Network connections
objref amparSyn, nmdarSyn // Synapses
objref hold, seClamp // Clamps
objref tvec, ivec, vvec // Recording vectors for time, current, and voltage
objref dvec // Recording vector for dendrite Vm
objref iampar,inmdar // Recording vector for AMPAR and NMDAR currents
objref outfile // Output file
objref impedance // For figuring out CC input impedance
objref saveFile // File to save individual traces to (see saveSynResp below)
objref rand // Random number generator
objref strFun // StringsFunction object
objref adOnPPpath // flags lookup vector for apical_dendrites
objref secRef // temporary section reference
objref nil // NULL object (so we can test for NULL, WTG hoc)
isError=0 // global error indicator
strdef curSecname // Name part of the current section (see parseSecname)
curSecnum=0 // Number part of the current section (see parseSecname)
nSomaSec=0 // Number of sections making up the soma
somaMidSec=0 // Index to section at the middle of the soma (roughly speaking)
strdef layer // layer of current segment as string
layerId=0 // layer as number: 0=SOMA, 1=AXON, 2=SO, 3=SL, 4=SR, 5=LM, -1=ERROR
ory3d=0 // reoriented y3d value for current segment
synLoc=0 // location of synapse in current section
segLen=0 // length of the current segment
ivec = new Vector() // record of soma current injection
vvec = new Vector() // record of soma voltage
tvec = new Vector() // record of time
dvec = new Vector() // record of voltage at the synapse
iampar = new Vector() // record AMPAR current
inmdar = new Vector() // record NMDAR current
adOnPPpath = new Vector(maxSecnum) // vector for onPPpath lookup by section number
strFun = new StringFunctions() // functional instance for string functions
// We will accumulate some stats by layer etc.
// Trunk segments are those in SR that have a PP connection in their subtree.
totSomaLen=0 // Total length of all soma segments
totSomaArea=0 // Total area of all soma segments
totAxonLen=0 // Total length of all axon segments
totAxonArea=0 // Total area of all axon segments
totLMlen=0 // Total length of all segments in LM
totLMarea=0 // Total area of all segments in LM
totSRlen=0 // Total length of all segments in SR
totSRarea=0 // Total area of all segments in SR
totSRTlen=0 // Total length of trunk segments in SR
totSRTarea=0 // Total area of all trunk segments in SR
totSOlen=0 // Total length of all segments in SO
totSOarea=0 // Total area of all segments in SO
// Create spine sections
create spineHead // area will be roughly 1 micron^2
create spineNeck // resistance will be roughly 500 mega-ohm
spineHead {L=0.5 diam=0.6 nseg=1}
spineNeck {L=1 diam=0.06 nseg=1}
spineNeck connect spineHead(0),1
// Load the cell topology, otherwise
// Neuron gets confused about sections.
// A common axon segment is added below.
xopen(cell)
// Remove any type 10 (marker) segments
if (section_exists("user10")) {
print "Removing user 10 segments"
forsec "user10" {
disconnect()
delete_section()
}
}
// Remove any axon segments
if (section_exists("axon")) {
print "Removing axon segments"
forsec "axon" {
disconnect()
delete_section()
}
}
// Now load a replacement axon segment
load_file(axonPath)
// ========================================================
// Subroutines (defined before they are referenced)
// ========================================================
// Utility routines for min/max of two values.
// Note that the documentation for proc even provides
// the code for this but there is no such min or max
// function outside of vectors.
func minVal() {if ($1<$2) { return $1 } else { return $2} }
func maxVal() {if ($1>$2) { return $1 } else { return $2} }
// Get the name and number of the current section. The
// section name is assumed to be in the form: name[num]
// Results are in globals curSecname and curSecnum.
proc parseSecname() {local n
strdef stemp
stemp=secname()
n = strFun.substr(stemp,"[")
curSecname=stemp
curSecnum=-1
if (n>=0) {
strFun.left(curSecname,n)
strFun.right(stemp,n)
sscanf(stemp,"[%d",&curSecnum)
}
}
// Find the layer associated with the current segment.
// xseg is the relative location within the section (0 to 1).
// ory3d is set based on segment location in the reoriented
// cell morphology using orientX, orientY, and orientZ.
// layer is set to one of "SOMA", "AXON", "SO", "SL", "SR",
// "LM", or "ERROR" with layerId set accordingly (0-5,-1).
// layerId is returned because you can set string values
// in hoc but cannot test their values except via strcmp.
// onPPpath is set to 1 if the current section is an
// apical_dendrite with a PP segment in its subtree.
// Otherwise onPPpath is 0. curSecname and curSecnum are
// set as a side-effect of calling parseSecname.
proc findLayer() {local xseg,xarc,ipt3d,al0,al1,sx3d,sy3d,sz3d
xseg = $1
layer = "ERROR" // no matching layer found (so far)
layerId=-1
onPPpath=0
// Determine if the current section is an
// apical_dendrite with a PP segment in its
// subtree. adTrunk vector is assumed to
// already be set with the appropriate values
parseSecname()
if (strcmp(curSecname,"apical_dendrite")==0) {
onPPpath=adOnPPpath.x(curSecnum)
}
// If the current section is a spineHead or spineNeck,
// assign an (so far unused) layer and id and stop
if (strcmp(curSecname,"spineNeck")==0 || strcmp(curSecname,"spineHead")==0) {
layer="SPINE"
layerId = 999
return
}
// Check for maximum allowed diameter.
// Larger dendrites are most likely to be
// really part of the soma regardless of type.
if (diam(xseg)>maxDendDiam || issection("soma.*")) {
layer = "SOMA"
layerId = 0
return
}
// Also look for any axon segments just in case
if (issection("axon.*")) {
layer = "AXON"
layerId = 1
return
}
// Locate 3D points for the current segment
// to get a Y-coordinate value.
xarc=xseg*L
ipt3d=1
while (ipt3d<n3d() && arc3d(ipt3d)<xarc) { ipt3d += 1 }
if (ipt3d<n3d()) {
// Estimate the segment center location
// from a linear interpolation of points
// before and after it in arc length.
al0=xarc-arc3d(ipt3d-1)
al1=arc3d(ipt3d)-xarc
sx3d=(al0*x3d(ipt3d)+al1*x3d(ipt3d-1))/(al0+al1)
sy3d=(al0*y3d(ipt3d)+al1*y3d(ipt3d-1))/(al0+al1)
sz3d=(al0*z3d(ipt3d)+al1*z3d(ipt3d-1))/(al0+al1)
// Get the distance along the reoriented y-coord
// orientX,Y,Z form a unit vector in the proper
// direction with the apical direction up (>0).
ory3d=sx3d*orientX+sy3d*orientY+sz3d*orientZ
// Classify layer based on oriented y-coord
if (ory3d>=PPy3d) {
layer="LM"
layerId=5
} else if (ory3d>=SRy3d) {
layer = "SR"
layerId=4
} else if (ory3d>SOy3d) {
layer = "SL"
layerId = 3
} else {
layer = "SO"
layerId = 2
}
} else {
print "arc3d mismatch at ",secname(),"(",xseg,")"
layer="ERROR"
layerId=-1
}
}
// Do initializations.
// This is repeated for each simulation pass,
// that is, once per synapse location simulated.
proc init() {
finitialize(Vrest)
fcurrent()
}
// Run a simulation pass for a single synapse.
// Results of the synaptic stimulation are analyzed
// and summary data is written to a file.
proc runIt() {
// Set clamps as appropriate for the recording mode.
// Note that CCinj is not recomputed here since impedance is
// a complicated procedure and the value should not change
// from one synapse to the other. Other redundant initializations
// contained here are to allow command line invocations with
// different parameters.
seClamp.dur1 = tError+tInit
seClamp.amp1 = VCvm
hold.dur=tError+tInit
hold.amp=0 // set to CCinj in runIt()
hold.del=10 // a short delay for visualization
if (isCC) {
seClamp.rs=1e15
hold.amp=CCinj
} else {
seClamp.rs=1
hold.amp=0
}
ns.start=tInit
tstop=tInit
cvode.maxstep(10)
run()
// Stimulated the synapse and record the
// results. For this, small time steps
// are need to get the respone kinetics.
cvode.maxstep(tDelta) // Set sample rate
stoprun=0
continuerun(tInit+tCont)
if (stoprun) {
isError=1
print "Stopped by request"
return
}
// Look at the response and extract peak value,
// time to peak, and half-height width. If the
// the initial recording did not capture enough
// data for the analysis, more run time is allowed
// in the loop below. First we get time to peak.
iMin=1e9
vMax=-1e9
dMax=-1e9
tPeak=0
kPeak=0
kInit=0
tRise1=0
tRise2=0
tRise=0
// Find the index of the end of the initialization period
// Note the soma equilibrium values for voltage
// and current. These are the baselines for EPSP/C.
for (k=0;k<tvec.size() && tvec.x[k]<=tInit; k+=1) {}
kInit=k-1
iRest=ivec.x[kInit] // rest soma current for VC
vRest=vvec.x[kInit] // rest soma voltage for CC
dRest=dvec.x[kInit] // rest synapse voltage
// Make sure that we have passed a peak. If not, keep running
// until there is an apparent peak or else we have reached the
// maximum allowed time.
isDone=0
while (!isDone) {
k=tvec.size()-1
if (k<0) {
print "Initial tvec is empty at ",secname(),synLoc
print "This should never occur"
isError=1
return
} else if (isCC==0) {
for (j=k;!isDone && j>kInit;j=j-1) {
if (ivec.x[j-1]<=ivec.x[j]) {
isDone = 1
}
}
} else if (isCC==1) {
for (j=k;!isDone && j>kInit;j=j-1) {
if (vvec.x[j-1]<=vvec.x[j]) {
isDone = 1
}
}
}
if (isDone) {
break
} else if (tvec.x[k]<tInit+tError) {
print "Continuing simulation at ",secname(),"(",synLoc,")"
stoprun=0
continuerun(t+tCont)
if (stoprun) {
isError=1
print "Stopped by request"
return
}
} else {
print "Failed to detect response peak for ",secname(),synLoc
isError=1
return
}
}
// Get peak response voltage at synapse
for (k=kInit; k<tvec.size(); k+=1) {
if (dvec.x[k]>dMax) {
dMax=dvec.x[k]
}
}
// For VC, get EPSC peak time, value, and rise time
if (isCC==0) {
// Locate the peak (negative relative to rest for EPSC)
for (k=kInit; k<tvec.size(); k+=1) {
if (ivec.x[k]<iMin) { // EPSC<0
iMin=ivec.x[k]
tPeak=tvec.x[k]
kPeak=k
}
}
// Get the 20% and 80% or peak rising time values
for (k=kInit; k<=kPeak; k+=1) {
if (ivec.x[k]>=0.8*iRest+0.2*iMin) {
tRise1=tvec.x[k]
iRise1=ivec.x[k]
}
if (ivec.x[k]>=0.2*iRest+0.8*iMin) {
tRise2=tvec.x[k]
iRise2=ivec.x[k]
}
}
// Adjust rise time based on actual values at granular time values
if (iRise2<iRise1) {
tRise=(tRise2-tRise1)*0.6*(iMin-iRest)/(iRise2-iRise1)
}
}
// For CC, get EPSP peak time, rise time, and value
if (isCC==1) {
// Locate the peak (postive relative to rest for EPSP)
for (k=kInit; k<tvec.size(); k+=1) {
if (vvec.x[k]>vMax) { // Note EPSP>0
vMax=vvec.x[k]
tPeak=tvec.x[k]
kPeak=k
}
}
// Get the 20% and 80% or peak rising time values
for (k=kInit; k<=kPeak; k+=1) {
if (vvec.x[k]<=0.8*vRest+0.2*vMax) {
tRise1=tvec.x[k]
vRise1=vvec.x[k]
}
if (vvec.x[k]<=0.2*vRest+0.8*vMax) {
tRise2=tvec.x[k]
vRise2=vvec.x[k]
}
}
// Adjust rise time based on actual values at granular time values
if (vRise2>vRise1) {
tRise=(tRise2-tRise1)*0.6*(vMax-vRest)/(vRise2-vRise1)
}
}
if (tPeak==0) {
isError=1
print "Failed to detect response peak for ",secname(),synLoc
print iMin,vMax,tPeak
}
// Get the rising (pre-peak) half-height time
thw1=0
thw2=0
isDone=0
if (isCC==0) for (k=kInit;k<=kPeak && !isDone;k+=1) {
if (ivec.x[k]<=(iMin+iRest)/2) {
thw1=tvec.x[k]
isDone=1
}
}
if (isCC==1) for (k=kInit;k<tvec.size() && !isDone;k+=1) {
if (vvec.x[k]>=(vMax+vRest)/2) {
thw1=tvec.x[k]
isDone=1
}
}
if (!isDone) {
isError=1
print "Failed to detect half-rise time for ",secname(),synLoc
}
// Now get the falling (post-peak) half-height time.
// If the current recording does not contain it,
// extend the simulation for additional time.
isDone=0
while (t<tError+tInit && !isDone) {
if (isCC==0) for (k=kInit;k<tvec.size() && isDone==0;k+=1) {
if (tvec.x[k]>=tPeak && ivec.x[k]>=(iMin+iRest)/2) {
thw2=tvec.x[k]
isDone=1
}
}
if (isCC==1) for (k=kInit;k<tvec.size() && isDone==0;k+=1) {
if (tvec.x[k]>=tPeak && vvec.x[k]<=(vMax+vRest)/2) {
thw2=tvec.x[k]
isDone=1
}
}
// See if we have detected the falling half-height point.
// If not, run the simulation further to try and locate it.
if (!isDone) {
print "Continuing simulation at ",secname(),"(",synLoc,")"
stoprun=0
continuerun(t+tCont)
if (stoprun) {
isError=1
print "Stopped by request"
return
}
}
}
if (!isDone) {
isError=1
print "Failed to detect half-fall time for ",secname(),synLoc
}
// If error found, do not continue here
if (isError) return
// Write the results in summary form to the output file
// and also print some values on the console log to distract any
// large semi-aquatic primates watching for their reward cue.
if (outfile!=nil) {
outfile.printf("%s,%g",secname(),synLoc)
outfile.printf(",%s,%g,%g",layer,ory3d,distance(synLoc))
outfile.printf(",%g,%g",segLen,segArea)
outfile.printf(",%g",onPPpath)
}
if (isCC) {
print "EPSP (mV) = ",vMax-vRest
if (outfile!=nil) outfile.printf(",CC,%g",vMax-vRest)
} else {
print "EPSC (pA) = ",(iMin-iRest)*1e3
if (outfile!=nil) outfile.printf(",VC,%g",(iMin-iRest)*1e3)
}
print "Rise time (ms) = ",tRise
print "Peak time (ms) = ",tPeak-tInit
print "Half-height width (ms) = ",thw2-thw1
print "Peak synaptic Vm (mV) = ",dMax
if (outfile!=nil) {
outfile.printf(",%g",tRise)
outfile.printf(",%g",tPeak-tInit)
outfile.printf(",%g",thw2-thw1)
outfile.printf(",%g,%g",dRest,dMax)
outfile.printf(",%g,%g,%g",amparNC.weight*1e3,amparSyn.tau1,amparSyn.tau2)
outfile.printf(",%g,%g,%g",nmdarNC.weight*1e3,nmdarSyn.tau1,nmdarSyn.tau2)
outfile.printf("\r\n") // Windows (and html) end of line
}
}
// Run just one synapse in the current section.
proc getSynResp() {local xseg
xseg = $1
segLen=L/nseg
// See where this segment is located
findLayer(xseg)
// Skip if this is soma, axon, SL,
// or otherwise an error.
if (layerId<=1 || layerId==3 || layerId>=100) { // SOMA, AXON, SL, Spine etc.
return
}
// Customize the synapse to the layer and apply
// any randomization rules to synapse properties.
// Even though Neuron provides normal and log-normal
// distributions, we do our own conversion from
// the default distribution for efficiency and
// in the general interest of knowing exactly what
// the results will be. Note that exp(x) where
// x~N(0,s^2) does not have a mean of 1. In general
// if we want exp(x) to have a mean of 1 we need to
// have x~N(-0.5*s^2,s^2). In this case the median
// of exp(x) is not the same as the mean.
rwm = 1
rtm = 1
if (layerId==2 || layerId==4) { // SO and SR
if (randomizeSyn) {
rwm = exp(rand.repick()*rcAMPARSynWsd-0.5*rcAMPARSynWsd^2)
rtm = exp(rand.repick()*rcAMPARSynTsd-0.5*rcAMPARSynTsd^2)
}
amparNC.weight=rcAMPARSynWeight*1e-3*rwm
amparSyn.tau1=minVal(rcAMPARSynTau1,rtm*rcAMPARSynTau2)
amparSyn.tau2=maxVal(rcAMPARSynTau1,rtm*rcAMPARSynTau2)
if (randomizeSyn) {
rwm = exp(rand.repick()*rcNMDARSynWsd-0.5^rcNMDARSynWsd^2)
rtm = exp(rand.repick()*rcNMDARSynTsd-0.5*rcNMDARSynTsd^2)
}
nmdarNC.weight=rcNMDARSynWeight*1e-3*rwm
nmdarSyn.tau1=minVal(rcNMDARSynTau1,rtm*rcNMDARSynTau2)
nmdarSyn.tau2=maxVal(rcNMDARSynTau1,rtm*rcNMDARSynTau2)
} else if (layerId==5) { // LM
if (randomizeSyn) {
rwm = exp(rand.repick()*ppAMPARSynWsd-0.5*ppAMPARSynWsd^2)
rtm = exp(rand.repick()*ppAMPARSynTsd-0.5*ppAMPARSynTsd^2)
}
amparNC.weight=ppAMPARSynWeight*1e-3*rwm
amparSyn.tau1=minVal(ppAMPARSynTau1,rtm*ppAMPARSynTau2)
amparSyn.tau2=maxVal(ppAMPARSynTau1,rtm*ppAMPARSynTau2)
if (randomizeSyn) {
rwm = exp(rand.repick()*ppNMDARSynWsd-0.5*ppNMDARSynWsd^2)
rtm = exp(rand.repick()*ppNMDARSynTsd-0.5*ppNMDARSynTsd^2)
}
nmdarNC.weight=ppNMDARSynWeight*1e-3*rwm
nmdarSyn.tau1=minVal(ppNMDARSynTau1,rtm*ppNMDARSynTau2)
nmdarSyn.tau2=maxVal(ppNMDARSynTau1,rtm*ppNMDARSynTau2)
} else {
print "Bad layerId value=",layerId
return
}
// Reset synapse conductance and [Mg++] based on
// types of synapses not blocked.
nmdarSyn.mg=lowExtMg // Assume low [Mg++] as the default
if (useAMPAR && useNMDAR) { // Both types of responses together
nmdarSyn.mg=highExtMg
} else if (useAMPAR) { // Only AMPAR
nmdarNC.weight=0
} else if (useNMDAR) { // Only NMDAR
amparNC.weight=0
} else {
print "Both AMPAR and NMDAR are blocked"
return
}
// Finish up with the synapses (this is redundant but safety first)
amparSyn.e=synErev
nmdarSyn.e=synErev
// Connect synapses with their postsynaptic element
// For LM synapses go directly on the dendrite while
// elsewhere, they are on the spine head if useSpine=1
synLoc=xseg
if (useSpine!=1 || layerId==5) {
amparSyn.loc(synLoc)
nmdarSyn.loc(synLoc)
} else {
// Customize the spine head for the equivalent of
// a K+ conductance of resistivity gspine.
// Also, make sure that the spine has an Ra
// to give a net 500 M-ohm axial resistance.
spineHead {
e_pas = (vRest/Rm + gkspine*ekspine)/(1/Rm+gkspine)
g_pas=1/Rm+gkspine
Ra=RaSpine
}
spineNeck { Ra=RaSpine }
// Connect synapse to the spine head and then
// connect the spine neck with the dendrite
spineHead{ amparSyn.loc(0.5) nmdarSyn.loc(0.5) }
spineNeck disconnect()
connect spineNeck(0),synLoc
soma[somaMidSec] distance(0,0.5)
}
// If AOK, go run something useful
if (!isError) {
segArea=area(synLoc)
dvec.record(&v(synLoc))
iampar.record(&arSyn.i)
inmdar.record(&nmdarSyn.i)
// Indicate where the synapse is located
printf("\n%s(%g) ",secname(),synLoc)
printf("layer=%s ory3d=%g dist=%g\n",layer,ory3d,distance(synLoc))
runIt()
}
// If using spines, disconnect the spine and
// restore distances to the original values.
if (useSpine==1) {
spineNeck disconnect()
soma[somaMidSec] distance(0,0.5)
}
}
// Simulate synaptic responses with a synapse
// placed in each segment, one at a time for all
// segments in SO, SR, or LM layers.
proc sampleSynResps() {
outfile = new File()
outfile.wopen(outPath)
// Write a header line to outfile
outfile.printf("%s","section,loc,layer,y,dist,len,area,trunk,type")
outfile.printf("%s",",peakValue,riseTime,peakTime,halfWidth,synRest,synPeak")
outfile.printf("%s",",gAMPAR,tau1AMPAR,tau2AMPAR,gNMDAR,tau1NMDAR,tau2NMDAR")
outfile.printf("\r\n") // Windows (and html) end of line
// Loop through all sections
forall {
// Loop through segments placing the synapse in
// appropriate segments and recording the synaptic
// response at that location.
for (xseg,0) {
// Get (and write) the response for a synapse
// at location xseg in the current section.
getSynResp(xseg)
}
}
outfile.close()
}
// Simulate a single synaptic activation and write the results
// to a file. This function would normally only be invoked manually.
// Invocation is saveSynResp(xseg). The path name of the file to save
// to is taken from global string savePath. Voltages are saved as mV
// and currents as pA. Time is in units of ms.
proc saveSynResp() {local xseg
xseg=$1
saveFile = new File()
saveFile.wopen(savePath)
tCont=200 // make sure whole response is captured
getSynResp(xseg)
// Write a CSV header line
saveFile.printf("time,soma,dend,iampar,inmdar\r\n")
// Write the appropriate data values.
if (isCC) { // save time and voltage, etc.
for(k=0; k<tvec.size(); k+=1) {
saveFile.printf("%g,%g,%g",tvec.x[k],vvec.x[k],dvec.x[k])
saveFile.printf(",%g,%g\r\n",1e3*iampar.x[k],1e3*inmdar.x[k])
}
} else { // save time and current, etc.
for(k=0; k<tvec.size(); k+=1) {
saveFile.printf("%g,%g,%g",tvec.x[k],1e3*ivec.x[k],dvec.x[k])
saveFile.printf(",%g,%g\r\n",1e3*iampar.x[k],1e3*inmdar.x[k])
}
}
saveFile.close()
}
// ================================
// End Subroutines
// ================================
// Find out how many sections make up the soma.
// Then look for the one with the largest diameter
// and designate it as the middle section.
forsec "soma" {nSomaSec=nSomaSec+1}
maxDiam=0
for (k=0;k<nSomaSec;k=k+1) soma[k] {
if (maxDiam<=diam) {
maxDiam=diam
somaMidSec=k
}
}
print "nSomaSec=",nSomaSec," somaMidSec=",somaMidSec
// In Neuromorpho SWC files, the soma is sometimes
// represented by a single point with the radius being
// the radius of a sphere of equivalent area. When
// CVAPP converts these files to HOC, the single point
// becomes two points with a small (0.01) difference in
// Z coordinates. Detect this problem and change the
// soma to a cylinder with area equivalent to the
// sphere intended in the SWC file. The cylinder is
// arbitrarily aligned along the Y axis.
access soma[somaMidSec]
if (nSomaSec==1) {
if (n3d()==2) {
if (abs(z3d(0)-z3d(1))<0.02) {
if (diam3d(0)==diam3d(1)) {
if (y3d(0)==y3d(1)) {
if (x3d(0)==x3d(1)) {
r = diam3d(0)/2
pt3dchange(0,x3d(0),y3d(0)-r,z3d(0),2*r)
pt3dchange(1,x3d(1),y3d(1)+r,z3d(1),2*r)
print "Soma area changed to ",4*PI*r^2
}}}}}}
// Set nseg so that each segment is at most seglen long.
// For this purpose, an odd number of segments is not
// important since we are not going to change nseg again.
// soma area(0.5) // from fixnseg.hoc -- what does it do?
forall {
nseg = int(1-1e-8+L/maxSegLen)
}
// Initialize the lookup table for detecting apical_dendrites
// that connect somewhere into PP. Each section in LM is found
// and that section and all parents in turn are flagged in adOnPPpath.
// This assumes that the most distal part of a section is in LM
// if any part of the section is (this should always be true).
forsec "apical_dendrite" {
findLayer(1) // test most distal part of the section
if (layerId==5) {
adOnPPpath.x(curSecnum)=1
while (curSecnum>=0) {
adOnPPpath.x(curSecnum)=1
apical_dendrite[curSecnum] {
secRef = new SectionRef()
secRef.parent parseSecname()
}
if (strcmp(curSecname,"apical_dendrite")!=0) break
}
}
}
// Create any special mechanisms. The soma is the
// context for the clamps, but the synapse is
// later relocated to different places as needed.
soma[somaMidSec] {
// Set up a voltage clamp to mimic experimental
// conditions prior to stimulation.
seClamp = new SEClamp(0.5)
seClamp.dur1 = tError+tInit
seClamp.amp1 = VCvm
// Just for fun, benchmark how long it takes for VC
// to propagate from the soma into dendrites.
// seClamp.dur1 = 500
// seClamp.amp1 = -65
// seClamp.dur2 = 500
// seClamp.amp2 = -80
hold = new IClamp(0.5)
hold.dur=tError+tInit