diff --git a/CHANGES.md b/CHANGES.md index b9d5e5060..c9871d8b1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,21 @@ +# Version in development + +**New features** + +- Raster plot colored by phase + +- Examples based on the CA3 model using the 'colorbyPhase' option in the plotRaster + +**Bug fixes** + +- Fixed loading point cell params from legacy models (issue 607) + +- Fix voltage movie tutorial + +- Fix to automatically include netstims in the sim.allSimData object when plotRaster 'include' selects 'all' + +- Fixed loading netParams in some scenarios (bug caused by srting functions pre-processing) + # Version 1.0.5 **New features** diff --git a/doc/source/user_documentation.rst b/doc/source/user_documentation.rst index 6b9daefac..8843f4c64 100644 --- a/doc/source/user_documentation.rst +++ b/doc/source/user_documentation.rst @@ -305,7 +305,7 @@ Currently, 'rhythmic', 'evoked', 'poisson' and 'gauss' spike pattern generators * **evoked** - creates the ongoing external inputs (rhythmic) - * **start** - time of first spike. if -1, uniform distribution between startMin and startMax (ms) + * **start** - time of first spike * **inc** - increase in time of first spike; from cfg.inc_evinput (ms) diff --git a/examples/CA3model_RasterColoredbyPhase/index.npjson b/examples/CA3model_RasterColoredbyPhase/index.npjson new file mode 100644 index 000000000..20c14b4a6 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/index.npjson @@ -0,0 +1,6 @@ +{ + "mod_folder": "mod", + "simConfig": "src/cfg.py", + "python_run": "src/init.py", + "netParams": "src/netParams.py" +} \ No newline at end of file diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod new file mode 100644 index 000000000..93d435e30 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ih.mod @@ -0,0 +1,64 @@ +: $Id: CA1ih.mod,v 1.4 2010/12/13 21:35:47 samn Exp $ +TITLE Ih CA3 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX hcurrent + NONSPECIFIC_CURRENT ih + RANGE g, e, v50, htau, hinf + RANGE gfactor +} + +PARAMETER { + celsius (degC) + g= 0.0001 (mho/cm2) + e= -30 (mV) + v50=-82 (mV) + gfactor = 1 +} + +STATE { + h +} + +ASSIGNED { + ih (mA/cm2) + hinf + htau (ms) + v (mV) +} + +PROCEDURE iassign () { ih=g*h*(v-e)*gfactor } + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + h'= (hinf- h)/ htau +} + +INITIAL { + rates(v) + h = hinf + iassign() +} + +PROCEDURE rates(v (mV)) { + UNITSOFF + : HCN1 + :hinf = 1/(1+exp(0.151*(v-v50))) + :htau = exp((0.033*(v+75)))/(0.011*(1+exp(0.083*(v+75)))) + + : HCN2 + hinf = 1/(1+exp((v-v50)/10.5)) + htau = (1/(exp(-14.59-0.086*v)+exp(-1.87+0.0701*v))) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod new file mode 100644 index 000000000..9e4fe6922 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ika.mod @@ -0,0 +1,85 @@ +: $Id: CA1ika.mod,v 1.2 2010/12/01 05:06:07 samn Exp $ +TITLE Ika CA1 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX kacurrent + NONSPECIFIC_CURRENT ika, ikad + RANGE g, gd, e, ninf, ntau, ndinf, ndtau, linf, ltau +} + +PARAMETER { + celsius (degC) + g= 0.048 (mho/cm2) + gd= 0 (mho/cm2) + e= -90 (mV) +} + +STATE { + n + nd : distal + l +} + +ASSIGNED { + v (mV) + ika (mA/cm2) + ikad (mA/cm2) + ninf + ntau (ms) + ndinf + ndtau (ms) + linf + ltau (ms) +} + +PROCEDURE iassign () { + ika=g*n*l*(v-e) + ikad=gd*nd*l*(v-e) +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + n'= (ninf- n)/ ntau + l'= (linf- l)/ ltau + nd'= (ndinf-nd)/ndtau +} + +INITIAL { + rates(v) + n = ninf + l = linf + iassign() +} + +PROCEDURE rates(v (mV)) { + LOCAL a, b + UNITSOFF + a = exp(-0.038*(1.5+1/(1+exp(v+40)/5))*(v-11)) + b = exp(-0.038*(0.825+1/(1+exp(v+40)/5))*(v-11)) + ntau=4*b/(1+a) + if (ntau<0.1) {ntau=0.1} + ninf=1/(1+a) + + a=exp(-0.038*(1.8+1/(1+exp(v+40)/5))*(v+1)) + b=exp(-0.038*(0.7+1/(1+exp(v+40)/5))*(v+1)) + ndtau=2*b/(1+a) + if (ndtau<0.1) {ndtau=0.1} + ndinf=1/(1+a) + + a = exp(0.11*(v+56)) + ltau=0.26*(v+50) + if (ltau<2) {ltau=2} + linf=1/(1+a) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod new file mode 100644 index 000000000..4c5236362 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ikdr.mod @@ -0,0 +1,60 @@ +: $Id: CA1ikdr.mod,v 1.2 2010/12/01 05:10:52 samn Exp $ +TITLE IKDR CA1 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX kdrcurrent + NONSPECIFIC_CURRENT ik + RANGE g, e, ninf, ntau +} + +PARAMETER { + celsius (degC) + g = 0.010 (mho/cm2) + e = -90 (mV) +} + +STATE { + n +} + +ASSIGNED { + v (mV) + ik (mA/cm2) + ninf + ntau (ms) +} + +PROCEDURE iassign () { ik=g*n*(v-e) } + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + n'= (ninf- n)/ ntau +} + +INITIAL { + rates(v) + n = ninf + iassign() +} + +PROCEDURE rates(v (mV)) { + LOCAL a, b + UNITSOFF + a = exp(-0.11*(v-13)) + b = exp(-0.08*(v-13)) + ntau=50*b/(1+a) + if (ntau<2) {ntau=2} + ninf=1/(1+a) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod b/examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod new file mode 100644 index 000000000..d33ab9739 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/CA1ina.mod @@ -0,0 +1,89 @@ +: $Id: CA1ina.mod,v 1.4 2010/11/30 19:50:00 samn Exp $ +TITLE INa CA1 + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) +} + +NEURON { + SUFFIX nacurrent + NONSPECIFIC_CURRENT ina + RANGE g, e, vi, ki + RANGE minf,hinf,iinf,mtau,htau,itau : testing +} + +PARAMETER { + : v (mV) + celsius (degC) + g = 0.032 (mho/cm2) + e = 55 (mV) + vi = -60 (mV) + ki = 0.8 +} + +STATE { + m + h + I : i +} + +ASSIGNED { + i (mA/cm2) + ina (mA/cm2) + minf + mtau (ms) + hinf + htau (ms) + iinf + itau (ms) + v (mV) : testing +} + +: PROCEDURE iassign () { ina=g*m*m*m*h*i*(v-e) } +PROCEDURE iassign () { i=g*m*m*m*h*I*(v-e) ina=i} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + m' = (minf - m) / mtau + h' = (hinf - h) / htau + : i' = (iinf - i) / itau + I' = (iinf - I) / itau +} + +INITIAL { + rates(v) + h = hinf + m = minf + : i = iinf + I = iinf + iassign() : testing +} + + +PROCEDURE rates(v (mV)) { + LOCAL a, b + UNITSOFF + a = 0.4*(v+30)/(1-exp(-(v+30)/7.2)) + b = 0.124*(v+30)/(exp((v+30)/7.2)-1) + mtau=0.5/(a+b) + if (mtau<0.02) {mtau=0.02} + minf=a/(a+b) + a = 0.03*(v+45)/(1-exp(-(v+45)/1.5)) + b = 0.01*(v+45)/(exp((v+45)/1.5)-1) + htau=0.5/(a+b) + if (htau<0.5) {htau=0.5} + hinf=1/(1+exp((v+50)/4)) + a = exp(0.45*(v+66)) + b = exp(0.09*(v+66)) + itau=3000*b/(1+a) + if (itau<10) {itau=10} + iinf=(1+ki*exp((v-vi)/2))/(1+exp((v-vi)/2)) + UNITSON +} + diff --git a/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod new file mode 100644 index 000000000..9a68baef1 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynBB.mod @@ -0,0 +1,67 @@ +: $Id: MyExp2SynBB.mod,v 1.4 2010/12/13 21:27:51 samn Exp $ +NEURON { +: THREADSAFE + POINT_PROCESS MyExp2SynBB + RANGE tau1, tau2, e, i, g, Vwt, gmax + NONSPECIFIC_CURRENT i +} + +UNITS { + (nA) = (nanoamp) + (mV) = (millivolt) + (uS) = (microsiemens) +} + +PARAMETER { + tau1=.1 (ms) <1e-9,1e9> + tau2 = 10 (ms) <1e-9,1e9> + e=0 (mV) + gmax = 1e9 (uS) + Vwt = 0 : weight for inputs coming in from vector +} + +ASSIGNED { + v (mV) + i (nA) + g (uS) + factor + etime (ms) +} + +STATE { + A (uS) + B (uS) +} + +INITIAL { + LOCAL tp + + Vwt = 0 : testing + + if (tau1/tau2 > .9999) { + tau1 = .9999*tau2 + } + A = 0 + B = 0 + tp = (tau1*tau2)/(tau2 - tau1) * log(tau2/tau1) + factor = -exp(-tp/tau1) + exp(-tp/tau2) + factor = 1/factor +} + +BREAKPOINT { + SOLVE state METHOD cnexp + g = B - A + if (g>gmax) {g=gmax}: saturation + i = g*(v - e) +} + +DERIVATIVE state { + A' = -A/tau1 + B' = -B/tau2 +} + +NET_RECEIVE(w (uS)) {LOCAL ww + ww=w + A = A + ww*factor + B = B + ww*factor +} diff --git a/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod new file mode 100644 index 000000000..01291643a --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/MyExp2SynNMDABB.mod @@ -0,0 +1,108 @@ +: $Id: MyExp2SynNMDABB.mod,v 1.4 2010/12/13 21:28:02 samn Exp $ +NEURON { +: THREADSAFE + POINT_PROCESS MyExp2SynNMDABB + RANGE tau1, tau2, e, i, iNMDA, s, sNMDA, r, tau1NMDA, tau2NMDA, Vwt, smax, sNMDAmax + NONSPECIFIC_CURRENT i, iNMDA +} + +UNITS { + (nA) = (nanoamp) + (mV) = (millivolt) + (uS) = (microsiemens) +} + +PARAMETER { + tau1 = 0.1 (ms) <1e-9,1e9> + tau2 = 10 (ms) <1e-9,1e9> + tau1NMDA = 15 (ms) + tau2NMDA = 150 (ms) + e = 0 (mV) + mg = 1 + r = 1 + smax = 1e9 (1) + sNMDAmax = 1e9 (1) + + Vwt = 0 : weight for inputs coming in from vector +} + +ASSIGNED { + v (mV) + i (nA) + iNMDA (nA) + s (1) + sNMDA (1) + mgblock (1) + factor (1) + factor2 (1) + + etime (ms) +} + +STATE { + A (1) + B (1) + A2 (1) + B2 (1) +} + +INITIAL { + + LOCAL tp + + Vwt = 0 : testing + + if (tau1/tau2 > .9999) { + tau1 = .9999*tau2 + } + A = 0 + B = 0 + tp = (tau1*tau2)/(tau2 - tau1) * log(tau2/tau1) + factor = -exp(-tp/tau1) + exp(-tp/tau2) + factor = 1/factor + + if (tau1NMDA/tau2NMDA > .9999) { + tau1NMDA = .9999*tau2NMDA + } + A2 = 0 + B2 = 0 + tp = (tau1NMDA*tau2NMDA)/(tau2NMDA - tau1NMDA) * log(tau2NMDA/tau1NMDA) + factor2 = -exp(-tp/tau1NMDA) + exp(-tp/tau2NMDA) + factor2 = 1/factor2 +} + +BREAKPOINT { + SOLVE state METHOD cnexp + : Jahr Stevens 1990 J. Neurosci + mgblock = 1.0 / (1.0 + 0.28 * exp(-0.062(/mV) * v) ) + s = B - A + sNMDA = B2 - A2 + if (s >smax) {s =smax }: saturation + if (sNMDA>sNMDAmax) {sNMDA=sNMDAmax}: saturation + i = s * (v - e) + iNMDA = sNMDA * (v - e) * mgblock +} + +DERIVATIVE state { + A' = -A/tau1 + B' = -B/tau2 + A2' = -A2/tau1NMDA + B2' = -B2/tau2NMDA +} + +NET_RECEIVE(w (uS)) {LOCAL ww + ww=w + :printf("NMDA Spike: %g\n", t) + if(r>=0){ : if r>=0, g = AMPA + NMDA*r + A = A + factor *ww + B = B + factor *ww + A2 = A2 + factor2*ww*r + B2 = B2 + factor2*ww*r + }else{ + if(r>-1000){ : if r>-1, g = NMDA*r + A2 = A2 - factor2*ww*r + B2 = B2 - factor2*ww*r + } + : if r<0 and r<>-1, g = 0 + } +} diff --git a/examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc b/examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc new file mode 100644 index 000000000..ccb579afb --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/aux_fun.inc @@ -0,0 +1,43 @@ +: $Id: aux_fun.inc,v 1.1 2009/11/04 01:24:52 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + + + +:------------------------------------------------------------------- +FUNCTION fun1(v(mV),V0(mV),A(/ms),B(mV))(/ms) { + + fun1 = A*exp((v-V0)/B) +} + +FUNCTION fun2(v(mV),V0(mV),A(/ms),B(mV))(/ms) { + + fun2 = A/(exp((v-V0)/B)+1) +} + +FUNCTION fun3(v(mV),V0(mV),A(/ms),B(mV))(/ms) { + + if(fabs((v-V0)/B)<1e-6) { + :if(v==V0) { + fun3 = A*B/1(mV) * (1- 0.5 * (v-V0)/B) + } else { + fun3 = A/1(mV)*(v-V0)/(exp((v-V0)/B)-1) + } +} + +FUNCTION min(x,y) { if (x<=y){ min = x }else{ min = y } } +FUNCTION max(x,y) { if (x>=y){ max = x }else{ max = y } } diff --git a/examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod new file mode 100644 index 000000000..3ea21a7ef --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/caolmw.mod @@ -0,0 +1,47 @@ +: $Id: caolmw.mod,v 1.2 2010/11/30 16:40:09 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mollar) = (1/liter) + (M) = (mollar) + (mM) = (millimollar) + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Caolmw + USEION ca READ ica, cai WRITE cai + RANGE alpha, tau +} + +PARAMETER { + alpha = 0.002 (cm2-M/mA-ms) + tau = 80 (ms) +} + +ASSIGNED { ica (mA/cm2) } + +INITIAL { cai = 0 } + +STATE { cai (mM) } + +BREAKPOINT { SOLVE states METHOD cnexp } + +DERIVATIVE states { cai' = -(1000) * alpha * ica - cai/tau } diff --git a/examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod new file mode 100644 index 000000000..51112d099 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/icaolmw.mod @@ -0,0 +1,51 @@ +: $Id: icaolmw.mod,v 1.2 2010/11/30 16:44:13 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX ICaolmw + USEION ca WRITE ica + RANGE gca,eca +} + +PARAMETER { + gca = 1 (mS/cm2) + eca = 120 (mV) +} + +ASSIGNED { + ica (mA/cm2) + v (mV) +} + +PROCEDURE iassign () { ica = (1e-3) * gca * mcainf(v)^2 * (v-eca) } + +INITIAL { + iassign() +} + +BREAKPOINT { iassign() } + +FUNCTION mcainf(v(mV)) { mcainf = fun2(v, -20, 1, -9)*1(ms) } + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod new file mode 100644 index 000000000..ccd919202 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/iholmw.mod @@ -0,0 +1,60 @@ +: $Id: iholmw.mod,v 1.2 2010/11/30 16:34:22 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Iholmw + NONSPECIFIC_CURRENT i + RANGE gh,eh +} + +PARAMETER { + gh = 0.15 (mS/cm2) + eh = -40 (mV) +} + +ASSIGNED { + v (mV) + i (mA/cm2) +} + +STATE { q } + +PROCEDURE iassign () { i = (1e-3) * gh * q * (v-eh) } + +INITIAL { + q = qinf(v) + iassign() +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { q' = (qinf(v)-q)/qtau(v) } + +FUNCTION qinf(v(mV)) { qinf = fun2(v, -80, 1, 10)*1(ms) } +FUNCTION qtau(v(mV))(ms) { qtau = 200(ms)/(exp((v+70(mV))/20(mV))+exp(-(v+70(mV))/20(mV))) + 5(ms) } + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod b/examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod new file mode 100644 index 000000000..b2368787e --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/kcaolmw.mod @@ -0,0 +1,52 @@ +: $Id: kcaolmw.mod,v 1.2 2010/11/30 16:47:18 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) + (mollar) = (1/liter) + (mM) = (millimollar) +} + +NEURON { + SUFFIX KCaolmw + USEION k WRITE ik + USEION ca READ cai + RANGE gkca,ek,kd +} + +PARAMETER { + gkca = 10 (mS/cm2) + ek = -90 (mV) + kd = 30 (mM) +} + +ASSIGNED { + cai (mM) + v (mV) + ik (mA/cm2) +} + +PROCEDURE iassign () { ik = (1e-3) * gkca * cai/(cai+kd) * (v-ek) } + +INITIAL { + iassign() +} + +BREAKPOINT { iassign() } diff --git a/examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod b/examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod new file mode 100644 index 000000000..fc52ae534 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/kdrbwb.mod @@ -0,0 +1,76 @@ +: $Id: kdrbwb.mod,v 1.4 2010/12/13 21:35:26 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Kdrbwb + USEION k WRITE ik + RANGE phin,gkdr,ek + RANGE taon,ninf +} + +PARAMETER { + gkdr = 9 (mS/cm2) + ek = -90 (mV) + phin = 5 +} + +ASSIGNED { + v (mV) + ik (mA/cm2) + celsius (degC) + ninf (1) + taon (ms) +} + +STATE { n } + +PROCEDURE iassign () { ik = (1e-3) * gkdr * n^4 * (v-ek) } + +INITIAL { + rates(v) + n = ninf + iassign() +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + n' = (ninf-n)/taon +} + +PROCEDURE rates(v(mV)) { LOCAL an, bn, q10 + q10 = phin:^((celsius-27.0(degC))/10.0(degC)) + + an = fun3(v, -34, -0.01, -10) + bn = fun1(v, -44, 0.125, -80) + + ninf = an/(an+bn) + taon = 1./((an+bn)*q10) +} + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod b/examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod new file mode 100644 index 000000000..37281dc94 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/mod/nafbwb.mod @@ -0,0 +1,81 @@ +: $Id: nafbwb.mod,v 1.4 2010/12/13 21:35:08 samn Exp $ +COMMENT + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE +// +// Copyright 2007, The University Of Pennsylvania +// School of Engineering & Applied Science. +// All rights reserved. +// For research use only; commercial use prohibited. +// Distribution without permission of Maciej T. Lazarewicz not permitted. +// mlazarew@seas.upenn.edu +// +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +ENDCOMMENT + +UNITS { + (mA) = (milliamp) + (mV) = (millivolt) + (mS) = (millisiemens) +} + +NEURON { + SUFFIX Nafbwb + USEION na WRITE ina + RANGE phih + RANGE gna, ena, taoh : testing +} + +PARAMETER { + gna = 35 (mS/cm2) + ena = 55 (mV) + phih = 5 +} + +ASSIGNED { + v (mV) + ina (mA/cm2) + minf (1) + hinf (1) + taoh (ms) + celsius (degC) +} + +STATE { h } + +PROCEDURE iassign () { ina = (1e-3) * gna * minf^3 * h * (v-ena) } + +INITIAL { + rates(v) + h = hinf + iassign() +} + +BREAKPOINT { + SOLVE states METHOD cnexp + iassign() +} + +DERIVATIVE states { + rates(v) + h' = (hinf-h)/taoh +} + +PROCEDURE rates(v(mV)) { LOCAL am, bm, ah, bh, q10 + + q10 = phih:^((celsius-27.0(degC))/10.0(degC)) + + am = fun3(v, -35, -0.1, -10) + bm = fun1(v, -60, 4, -18) + minf = am/(am+bm) + + ah = fun1(v, -58, 0.07, -20) + bh = fun2(v, -28, 1, -10) + hinf = ah/(ah+bh) + taoh = 1./((ah+bh)*q10) +} + +INCLUDE "aux_fun.inc" diff --git a/examples/CA3model_RasterColoredbyPhase/src/cfg.py b/examples/CA3model_RasterColoredbyPhase/src/cfg.py new file mode 100644 index 000000000..85483afd6 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/src/cfg.py @@ -0,0 +1,34 @@ +from netpyne import specs + +cfg = specs.SimConfig() + +cfg.duration = 750 +cfg.dt = 0.1 +cfg.hparams = {'v_init': -65.0} +cfg.verbose = False +cfg.recordTraces = {'V_soma':{'sec':'soma','loc':0.5,'var':'v'}} # Dict with traces to record +cfg.recordStim = False +cfg.recordStep = 0.1 # Step size in ms to save data (eg. V traces, LFP, etc) +cfg.filename = '00' # Set file output name +cfg.savePickle = False # Save params, network and sim output to pickle file +cfg.saveDat = False +cfg.printRunTime = 0.1 +cfg.recordLFP = [[50, 50, 50]] + +cfg.analysis['plotRaster']={ + 'colorbyPhase':{ +# 'signal': LFP_array, # when signal is provided as a np.array +# 'signal': 'LFP_signal.pkl', # when signal is provided as a list in a .pkl +# 'fs': 10000, # in both cases, sampling frequency would be neccessary + 'signal': 'LFP', # internal signal, from the computation of LFPs + 'electrode':1, + 'filtFreq':[4,8], + 'filtOrder': 3, + 'pop_background': True, + 'include_signal': True + }, + 'include':['allCells'], + 'saveFig':True, 'timeRange': [100,600]} # Plot a raster +cfg.analysis['plotTraces'] = {'include': [0, 1, 800, 801, 1000, 1001], 'saveFig': True} # Plot recorded traces for this list of cells +cfg.analysis['plotLFPTimeSeries'] = {'showFig': True, 'saveFig': True} +#cfg.analysis['plotShape'] = {'includePre': ['all'],'includePost': [0,800,1000],'cvar':'numSyns','dist':0.7, 'saveFig': True} diff --git a/examples/CA3model_RasterColoredbyPhase/src/init.py b/examples/CA3model_RasterColoredbyPhase/src/init.py new file mode 100644 index 000000000..5c371b5c4 --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/src/init.py @@ -0,0 +1,10 @@ +""" +init.py + +Starting script to run NetPyNE-based CA3 model. +""" + +from netpyne import sim + +cfg, netParams = sim.loadFromIndexFile('index.npjson') +sim.createSimulateAnalyze(netParams, cfg) diff --git a/examples/CA3model_RasterColoredbyPhase/src/netParams.py b/examples/CA3model_RasterColoredbyPhase/src/netParams.py new file mode 100644 index 000000000..083a02dde --- /dev/null +++ b/examples/CA3model_RasterColoredbyPhase/src/netParams.py @@ -0,0 +1,322 @@ +from netpyne import specs +try: + from __main__ import cfg +except: + from cfg import cfg + +# Network parameters +netParams = specs.NetParams() # object of class NetParams to store the network parameters +netParams.defaultThreshold = 0.0 +netParams.defineCellShapes = True # sets 3d geometry aligned along the y-axis + + +############################################################################### +## Cell types +############################################################################### +# Basket cell +BasketCell = {'secs':{}} +BasketCell['secs']['soma'] = {'geom': {}, 'mechs': {}} +BasketCell['secs']['soma']['geom'] = {'diam': 100, 'L': 31.831, 'nseg': 1, 'cm': 1} +BasketCell['secs']['soma']['mechs'] = {'pas': {'g': 0.1e-3, 'e': -65}, 'Nafbwb': {}, 'Kdrbwb': {}} +netParams.cellParams['BasketCell'] = BasketCell + + +# OLM cell +OlmCell = {'secs':{}} +OlmCell['secs']['soma'] = {'geom': {}, 'mechs': {}} +OlmCell['secs']['soma']['geom'] = {'diam': 100, 'L': 31.831, 'nseg': 1, 'cm': 1} +OlmCell['secs']['soma']['mechs'] = { + 'pas': {'g': 0.1e-3, 'e': -65}, + 'Nafbwb': {}, + 'Kdrbwb': {}, + 'Iholmw': {}, + 'Caolmw': {}, + 'ICaolmw': {}, + 'KCaolmw': {}} +netParams.cellParams['OlmCell'] = OlmCell + + +# Pyramidal cell +PyrCell = {'secs':{}} +PyrCell['secs']['soma'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['soma']['geom'] = {'diam': 20, 'L': 20, 'cm': 1, 'Ra': 150} +PyrCell['secs']['soma']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {}, + 'kacurrent': {}, + 'kdrcurrent': {}, + 'hcurrent': {}} +PyrCell['secs']['Bdend'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Bdend']['geom'] = {'diam': 2, 'L': 200, 'cm': 1, 'Ra': 150} +PyrCell['secs']['Bdend']['topol'] = {'parentSec': 'soma', 'parentX': 0, 'childX': 0} +PyrCell['secs']['Bdend']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {'ki': 1}, + 'kacurrent': {}, + 'kdrcurrent': {}, + 'hcurrent': {}} +PyrCell['secs']['Adend1'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Adend1']['geom'] = {'diam': 2, 'L': 150, 'cm': 1, 'Ra': 150} +PyrCell['secs']['Adend1']['topol'] = {'parentSec': 'soma', 'parentX': 1.0, 'childX': 0} # here there is a change: connected to end soma(1) instead of soma(0.5) +PyrCell['secs']['Adend1']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {'ki': 0.5}, + 'kacurrent': {'g': 0.072}, + 'kdrcurrent': {}, + 'hcurrent': {'v50': -82, 'g': 0.0002}} +PyrCell['secs']['Adend2'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Adend2']['geom'] = {'diam': 2, 'L': 150, 'cm': 1, 'Ra': 150} +PyrCell['secs']['Adend2']['topol'] = {'parentSec': 'Adend1', 'parentX': 1, 'childX': 0} +PyrCell['secs']['Adend2']['mechs'] = { + 'pas': {'g': 0.0000357, 'e': -70}, + 'nacurrent': {'ki': 0.5}, + 'kacurrent': {'g': 0, 'gd': 0.120}, + 'kdrcurrent': {}, + 'hcurrent': {'v50': -90, 'g': 0.0004}} +PyrCell['secs']['Adend3'] = {'geom': {}, 'mechs': {}} +PyrCell['secs']['Adend3']['geom'] = {'diam': 2, 'L': 150, 'cm': 2, 'Ra': 150} +PyrCell['secs']['Adend3']['topol'] = {'parentSec': 'Adend2', 'parentX': 1, 'childX': 0} +PyrCell['secs']['Adend3']['mechs'] = { + 'pas': {'g': 0.0000714, 'e': -70}, + 'nacurrent': {'ki': 0.5}, + 'kacurrent': {'g': 0, 'gd': 0.200}, + 'kdrcurrent': {}, + 'hcurrent': {'v50': -90, 'g': 0.0007}} +netParams.cellParams['PyrCell'] = PyrCell + + +############################################################################### +## Synaptic mechs +############################################################################### + +netParams.synMechParams['AMPAf'] = {'mod': 'MyExp2SynBB', 'tau1': 0.05, 'tau2': 5.3, 'e': 0} +netParams.synMechParams['NMDA'] = {'mod': 'MyExp2SynNMDABB', 'tau1': 0.05, 'tau2': 5.3, 'tau1NMDA': 15, 'tau2NMDA': 150, 'r': 1, 'e': 0} +netParams.synMechParams['GABAf'] = {'mod': 'MyExp2SynBB', 'tau1': 0.07, 'tau2': 9.1, 'e': -80} +netParams.synMechParams['GABAs'] = {'mod': 'MyExp2SynBB', 'tau1': 0.2, 'tau2': 20, 'e': -80} +netParams.synMechParams['GABAss'] = {'mod': 'MyExp2SynBB', 'tau1': 20, 'tau2': 40, 'e': -80} + + +############################################################################### +## Populations +############################################################################### +netParams.popParams['PYR'] = {'cellType': 'PyrCell', 'numCells': 800} +netParams.popParams['BC'] = {'cellType': 'BasketCell', 'numCells': 200} +netParams.popParams['OLM'] = {'cellType': 'OlmCell', 'numCells': 200} + + +############################################################################### +# Current-clamp to cells +############################################################################### +netParams.stimSourceParams['IClamp_PYR'] = {'type': 'IClamp', 'del': 2*cfg.dt, 'dur': 1e9, 'amp': 50e-3} +netParams.stimSourceParams['IClamp_OLM'] = {'type': 'IClamp', 'del': 2*cfg.dt, 'dur': 1e9, 'amp': -25e-3} + +netParams.stimTargetParams['IClamp_PYR->PYR'] = { + 'source': 'IClamp_PYR', + 'sec': 'soma', + 'loc': 0.5, + 'conds': {'pop': 'PYR'}} + +netParams.stimTargetParams['IClamp_OLM->OLM'] = { + 'source': 'IClamp_OLM', + 'sec': 'soma', + 'loc': 0.5, + 'conds': {'pop': 'OLM'}} + + +############################################################################### +# Setting connections +############################################################################### + +# PYR -> X, NMDA +netParams.connParams['PYR->BC_NMDA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'BC'}, + 'convergence': 100, + 'weight': 1.38e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'NMDA'} + +netParams.connParams['PYR->OLM_NMDA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'OLM'}, + 'convergence': 10, + 'weight': 0.7e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'NMDA'} + +netParams.connParams['PYR->PYR_NMDA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 25, + 'weight': 0.004e-3, + 'delay': 2, + 'sec': 'Bdend', + 'loc': 1.0, + 'synMech': 'NMDA'} + +# PYR -> X, AMPA +netParams.connParams['PYR->BC_AMPA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'BC'}, + 'convergence': 100, + 'weight': 0.36e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'AMPAf'} + +netParams.connParams['PYR->OLM_AMPA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'OLM'}, + 'convergence': 10, + 'weight': 0.36e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'AMPAf'} + +netParams.connParams['PYR->PYR_AMPA'] = {'preConds': {'pop': 'PYR'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 25, + 'weight': 0.02e-3, + 'delay': 2, + 'sec': 'Bdend', + 'loc': 1.0, + 'synMech': 'AMPAf'} + +# BC -> X, GABA +netParams.connParams['BC->BC_GABA'] = {'preConds': {'pop': 'BC'}, 'postConds': {'pop': 'BC'}, + 'convergence': 60, + 'weight':4.5e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'GABAf'} + +netParams.connParams['BC->PYR_GABA'] = {'preConds': {'pop': 'BC'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 50, + 'weight': 0.72e-3, + 'delay': 2, + 'sec': 'soma', + 'loc': 0.5, + 'synMech': 'GABAf'} + + +# OLM -> PYR, GABA +netParams.connParams['OLM->PYR_GABA'] = {'preConds': {'pop': 'OLM'}, 'postConds': {'pop': 'PYR'}, + 'convergence': 20, + 'weight': 72e-3, + 'delay': 2, + 'sec': 'Adend2', + 'loc': 0.5, + 'synMech': 'GABAs'} + + +############################################################################### +# Setting NetStims +############################################################################### +# to PYR +netParams.stimSourceParams['NetStim_PYR_SOMA_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_SOMA_AMPA->PYR'] = { + 'source': 'NetStim_PYR_SOMA_AMPA', + 'conds': {'pop': 'PYR'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 4*0.05e-3, # different from published value + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_PYR_ADEND3_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_ADEND3_AMPA->PYR'] = { + 'source': 'NetStim_PYR_ADEND3_AMPA', + 'conds': {'pop': 'PYR'}, + 'sec': 'Adend3', + 'loc': 0.5, + 'weight': 4*0.05e-3, # different from published value + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_PYR_SOMA_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_SOMA_GABA->PYR'] = { + 'source': 'NetStim_PYR_SOMA_GABA', + 'conds': {'pop': 'PYR'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.012e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +netParams.stimSourceParams['NetStim_PYR_ADEND3_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_ADEND3_GABA->PYR'] = { + 'source': 'NetStim_PYR_ADEND3_GABA', + 'conds': {'pop': 'PYR'}, + 'sec': 'Adend3', + 'loc': 0.5, + 'weight': 0.012e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +netParams.stimSourceParams['NetStim_PYR_ADEND3_NMDA'] = {'type': 'NetStim', 'interval': 100, 'number': int((1000/100.0)*cfg.duration), 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_PYR_ADEND3_NMDA->PYR'] = { + 'source': 'NetStim_PYR_ADEND3_NMDA', + 'conds': {'pop': 'PYR'}, + 'sec': 'Adend3', + 'loc': 0.5, + 'weight': 6.5e-3, + 'delay': 2*cfg.dt, + 'synMech': 'NMDA'} + +# to BC +netParams.stimSourceParams['NetStim_BC_SOMA_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_BC_SOMA_AMPA->BC'] = { + 'source': 'NetStim_BC_SOMA_AMPA', + 'conds': {'pop': 'BC'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.02e-3, + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_BC_SOMA_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_BC_SOMA_GABA->BC'] = { + 'source': 'NetStim_BC_SOMA_GABA', + 'conds': {'pop': 'BC'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.2e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +# to OLM +netParams.stimSourceParams['NetStim_OLM_SOMA_AMPA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_OLM_SOMA_AMPA->OLM'] = { + 'source': 'NetStim_OLM_SOMA_AMPA', + 'conds': {'pop': 'OLM'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.0625e-3, + 'delay': 2*cfg.dt, + 'synMech': 'AMPAf'} + +netParams.stimSourceParams['NetStim_OLM_SOMA_GABA'] = {'type': 'NetStim', 'interval': 1, 'number': 1000*cfg.duration, 'start': 0, 'noise': 1} +netParams.stimTargetParams['NetStim_OLM_SOMA_GABA->OLM'] = { + 'source': 'NetStim_OLM_SOMA_GABA', + 'conds': {'pop': 'OLM'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 0.2e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAf'} + +# Medial Septal inputs to BC and OLM cells +netParams.stimSourceParams['Septal'] = {'type': 'NetStim', 'interval': 150, 'number': int((1000/150)*cfg.duration), 'start': 0, 'noise': 0} +netParams.stimTargetParams['Septal->BC'] = { + 'source': 'Septal', + 'conds': {'pop': 'BC'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 1.6e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAss'} + +netParams.stimTargetParams['Septal->OLM'] = { + 'source': 'Septal', + 'conds': {'pop': 'OLM'}, + 'sec': 'soma', + 'loc': 0.5, + 'weight': 1.6e-3, + 'delay': 2*cfg.dt, + 'synMech': 'GABAss'} diff --git a/netpyne/analysis/spikes.py b/netpyne/analysis/spikes.py index bfc510199..39bb9c403 100644 --- a/netpyne/analysis/spikes.py +++ b/netpyne/analysis/spikes.py @@ -49,7 +49,7 @@ def prepareSpikeData( fileDesc=None, fileType=None, fileDir=None, - calculatePhase=False, + colorbyPhase=None, **kwargs ): """ @@ -157,6 +157,105 @@ def prepareSpikeData( sel = pd.concat([sel, ns]) numNetStims += len(spkindsNew) + # Calculating Phase of spikes + if isinstance(colorbyPhase,dict): + + try: + Signal = colorbyPhase['signal'] + except: + print("Importing signal for coloring spikes - No information about the signal") + Signal = 'LFP' + + if isinstance(Signal, basestring) or isinstance(Signal,np.ndarray): + from scipy import signal, stats, interpolate + from scipy.signal import hilbert + from math import fmod, pi + + rawSignal = [] + if isinstance(Signal, basestring): + # signal provided as a list packed in a pkl + if Signal.endswith('.pkl'): + import pickle + + with open(Signal, 'rb') as input_file: + rawSignal = pickle.load(input_file) + + try: + fs = colorbyPhase['fs'] + except: + print("Importing signal for coloring spikes - No frequency sampling provided") + fs = 1000.0 # assumes data sampled in ms + + time = np.linspace(0,len(rawSignal)*1000/fs,len(rawSignal)+1) # in milliseconds + + elif Signal == 'LFP': + try: + electrode = colorbyPhase['electrode'] + if electrode > sim.net.recXElectrode.nsites: + print('Wrong electrode number for coloring spikes according to LFP phase - Assigning first element in LFP recording setup') + electrode = 1 + except: + electrode = 1 + + rawSignal = [sim.allSimData['LFP'][n][electrode-1] for n in range(len(sim.allSimData['LFP']))] + fs = 1000.0/sim.cfg.recordStep + time = np.linspace(0,len(rawSignal)*1000/fs,len(rawSignal)+1) # in milliseconds + + else: + print('No signal recovered to color spikes according to its phase') + + # it is an array + else: + rawSignal = Signal.tolist() + try: + fs = colorbyPhase['fs'] + except: + print("Importing signal for coloring spikes - No frequency sampling provided") + fs = 1000.0 # assumes data sampled in ms + + time = np.linspace(0,len(rawSignal)*1000/fs,len(rawSignal)+1) # in milliseconds + + # Processing rawSignal + rawSignal.append(2*rawSignal[-1]-rawSignal[-2]) # extrapolation for the last element + rawSignal = stats.zscore(np.float32(rawSignal)) + rawSignal_ = np.r_[rawSignal[-1::-1],rawSignal,rawSignal[-1::-1]] # Reflect signal to minimize edge artifacts + + nyquist = fs/2.0 + + # parameters provided to filter the signal - setting defaults otherwise + try: + filtOrder = colorbyPhase['filtOrder'] + except: + filtOrder = 3 + + try: + filtFreq = colorbyPhase['filtFreq'] + except: + filtFreq = [1,500] + + if isinstance(filtFreq, list): # bandpass + Wn = [filtFreq[0]/nyquist, filtFreq[1]/nyquist] + b, a = signal.butter(filtOrder, Wn, btype='bandpass') + + elif isinstance(filtFreq, Number): # lowpass + Wn = filtFreq/nyquist + b, a = signal.butter(filtOrder, Wn) + + rawSignalFiltered_ = signal.filtfilt(b, a, rawSignal_) + + analytic_signal = hilbert(rawSignalFiltered_)[len(rawSignal):-len(rawSignal)] + amplitude_envelope = np.abs(analytic_signal) + instantaneous_phase = np.unwrap(np.angle(analytic_signal)) + instantaneous_phase_mod = [(fmod(instantaneous_phase[nn]+pi,2*pi)-pi)*(180/pi) for nn in range(len(instantaneous_phase))] + instantaneous_phase = np.r_[instantaneous_phase_mod] + + f_Rhythm = interpolate.interp1d(time,instantaneous_phase) + + sel['spkPhase'] = sel['spkt'].apply(f_Rhythm) + + else: + print('No signal recovered to color spikes according to its phase') + if len(cellGids) > 0 and numNetStims: ylabelText = ylabelText + ' and NetStims (at the end)' elif numNetStims: @@ -277,6 +376,12 @@ def prepareSpikeData( 'axisArgs': axisArgs, 'legendLabels': legendLabels, } + + if colorbyPhase: + spikeData.update({'spkPhases': sel['spkPhase'].tolist()}) + if 'include_signal' in colorbyPhase and colorbyPhase['include_signal']==True: + spikeData.update({'signal': analytic_signal}) + spikeData.update({'time': time}) if saveData: saveFigData(spikeData, fileName=fileName, fileDesc='spike_data', fileType=fileType, fileDir=fileDir, sim=sim) @@ -292,6 +397,7 @@ def prepareRaster( maxSpikes=1e8, orderBy='gid', popRates=True, + colorbyPhase=None, saveData=False, fileName=None, fileDesc=None, @@ -311,6 +417,7 @@ def prepareRaster( maxSpikes=maxSpikes, orderBy=orderBy, popRates=popRates, + colorbyPhase=colorbyPhase, saveData=saveData, fileName=fileName, fileDesc=fileDesc if fileDesc else 'raster_data', diff --git a/netpyne/batch/batch.py b/netpyne/batch/batch.py index 31fd07b99..0cb831917 100644 --- a/netpyne/batch/batch.py +++ b/netpyne/batch/batch.py @@ -75,9 +75,38 @@ def tupleToStr(obj): # ------------------------------------------------------------------------------- class Batch(object): """ - Class for/to - - + Class that handles batch simulations on NetPyNE. + Relevant Attributes: + batchLabel : str + The label of the batch used for directory/file naming of batch generated files. + cfgFile : str + The path of the file containing the `netpyne.simConfig.SimConfig` object + cfg : `netpyne.simConfig.SimConfig` + The `netpyne.simConfig.SimConfig` object + N.B. either cfg or cfgFile should be specified #TODO: replace with typechecked single argument + netParamsFile : str + The path of the file containing the `netpyne.netParams.NetParams` object + netParams : `netpyne.netParams.NetParams` + The `netpyne.netParams.NetParams` object + N.B. either netParams or netParamsFile should be specified #TODO: replace with typechecked single argument + initCfg : dict + params dictionary that is used to modify the batch cfg prior to any algorithm based parameter modifications + saveFolder : str + The path of the folder where the batch will be saved (defaults to batchLabel) + method : str + The algorithm method used for batch + runCfg : dict + Keyword: Arg dictionary used to generate submission templates (see utils.py) + evolCfg : dict #TODO: replace with algoCfg? to merge with optimCfg + Keyword: Arg dictionary used to define evolutionary algorithm parameters (see evol.py) + optimCfg : dict #TODO: replace with algoCfg? to merge with evolCfg + Keyword: Arg dictionary used to define optimization algorithm parameters + (see asd_parallel.py, optuna_parallel.py, sbi_parallel.py) + params : list + Dictionary of parameters to be explored per algorithm (grid, evol, asd, optuna, sbi) + (see relevant algorithm script for details) + seed : int + Seed for random number generator for some algorithms """ def __init__( @@ -88,19 +117,23 @@ def __init__( netParams=None, params=None, groupedParams=None, - initCfg={}, + initCfg=None, seed=None, ): self.batchLabel = 'batch_' + str(datetime.date.today()) self.cfgFile = cfgFile self.cfg = cfg self.netParams = netParams - self.initCfg = initCfg + if initCfg: + self.initCfg = initCfg + else: + self.initCfg = {} self.netParamsFile = netParamsFile self.saveFolder = '/' + self.batchLabel self.method = 'grid' self.runCfg = {} self.evolCfg = {} + self.optimCfg = {} self.params = [] self.seed = seed if params: diff --git a/netpyne/batch/grid.py b/netpyne/batch/grid.py index 6ea987cf1..adec7a77f 100644 --- a/netpyne/batch/grid.py +++ b/netpyne/batch/grid.py @@ -340,7 +340,7 @@ def gridSubmit(batch, pc, netParamsSavePath, jobName, simLabel, processes, proce 'walltime': walltime, 'vmem': '32G', 'queueName': 'cpu.q', - 'cores': 1, + 'cores': 2, 'pre': '', 'post': '', 'mpiCommand': 'mpiexec', #'log': "~/qsub/{}".format(jobName) @@ -350,7 +350,7 @@ def gridSubmit(batch, pc, netParamsSavePath, jobName, simLabel, processes, proce sge_args.update(batch.runCfg) #(batch, pc, netParamsSavePath, jobName, simLabel, processes, processFiles): - sge_args['command'] = '%s -n $NSLOTS nrniv -python -mpi %s simConfig=%s netParams=%s' % ( + sge_args['command'] = '%s -n $NSLOTS -hosts $(hostname) nrniv -python -mpi %s simConfig=%s netParams=%s' % ( sge_args['mpiCommand'], script, cfgSavePath, diff --git a/netpyne/batch/utils.py b/netpyne/batch/utils.py index 30b4aa361..9e65281ba 100644 --- a/netpyne/batch/utils.py +++ b/netpyne/batch/utils.py @@ -459,6 +459,8 @@ def jobPathAndNameForCand(index): elif type == 'hpc_sge': executer = 'qsub' queueName = args.get('queueName', 'default') + command = '%s -n $NSLOTS -hosts $(hostname) %s -python -mpi %s simConfig=%s netParams=%s' % ( + mpiCommand, nrnCommand, script, cfgSavePath, netParamsSavePath) jobString = jobStringHPCSGE( jobName, walltime, queueName, nodes, coresPerNode, jobPath, custom, command ) diff --git a/netpyne/cell/cell.py b/netpyne/cell/cell.py index 171438510..de12a5923 100644 --- a/netpyne/cell/cell.py +++ b/netpyne/cell/cell.py @@ -186,29 +186,8 @@ def recordTraces(self): conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # choose what to comapare to - if condKey in ['gid']: # CHANGE TO GID - compareTo = self.gid - else: - compareTo = self.tags[condKey] - - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if compareTo == self.gid: - if compareTo not in condVal: - conditionsMet = 0 - break - elif compareTo < condVal[0] or compareTo > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if compareTo not in condVal: - conditionsMet = 0 - break - elif compareTo != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['conds']) + if conditionsMet: try: ptr = None @@ -373,6 +352,12 @@ def recordTraces(self): # else: # if sim.cfg.verbose: print ' NOT recording ', key, 'from cell ', self.gid, ' with parameters: ',str(params) + + def checkConditions(self, conditions): + from ..sim.utils import checkConditions + return checkConditions(conditions=conditions, against=self.tags, cellGid=self.gid) + + def _randomizer(self): try: return self.__cellParamsRand diff --git a/netpyne/cell/compartCell.py b/netpyne/cell/compartCell.py index a44a73c1a..d0f1c4646 100644 --- a/netpyne/cell/compartCell.py +++ b/netpyne/cell/compartCell.py @@ -90,24 +90,19 @@ def create(self, createNEURONObj=None): for propLabel, prop in sim.net.params.cellParams.items(): # for each set of cell properties conditionsMet = 1 if 'conds' in prop and len(prop['conds']) > 0: - for (condKey, condVal) in prop['conds'].items(): # check if all conditions are met - if isinstance(condVal, list): - if isinstance(condVal[0], Number): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal[0], basestring): - if self.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(prop['conds']) elif self.tags['cellType'] != propLabel: # simplified method for defining cell params (when no 'conds') conditionsMet = False if conditionsMet: # if all conditions are met, set values for this cell + + # Intercept possible issue where the cell is designed to be PointCell but misclassfied as CompartCell in Pop._setCellClass() + # (may happen if .mod not compiled or mech name misspelled) + assert 'secs' in prop, \ + f"""Cell rule labeled '{propLabel}' is a compartment cell, but it doesn't have required entry 'secs'. +If this cell is expected to be a point cell instead, make sure the correspondent mechanism is included and compiled.""" + if sim.cfg.includeParamsLabel: if 'label' not in self.tags: self.tags['label'] = [propLabel] # create list of property sets @@ -125,32 +120,14 @@ def create(self, createNEURONObj=None): def modify(self, prop): from .. import sim - conditionsMet = 1 - for (condKey, condVal) in prop['conds'].items(): # check if all conditions are met - if condKey == 'label': - if condVal not in self.tags['label']: - conditionsMet = 0 - break - elif isinstance(condVal, list): - if isinstance(condVal[0], Number): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal[0], basestring): - if self.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(prop['conds']) if conditionsMet: # if all conditions are met, set values for this cell if sim.cfg.createPyStruct: self.createPyStruct(prop) if sim.cfg.createNEURONObj: - self.createNEURONObj( - prop - ) # add sections, mechanisms, synaptic mechanisms, geometry and topolgy specified by this property set + # add sections, mechanisms, synaptic mechanisms, geometry and topolgy specified by this property set + self.createNEURONObj(prop) def createPyStruct(self, prop): from .. import sim @@ -840,45 +817,28 @@ def modifySynMechs(self, params): conditionsMet = 1 if 'cellConds' in params: - if conditionsMet: - for (condKey, condVal) in params['cellConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['cellConds']) if conditionsMet: for secLabel, sec in self.secs.items(): for synMech in sec['synMechs']: conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # check if conditions met - if condKey == 'sec': - if condVal != secLabel: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], Number): - if synMech.get(condKey) < condVal[0] or synMech.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if synMech.get(condKey) not in condVal: - conditionsMet = 0 - break - elif synMech.get(condKey) != condVal: - conditionsMet = 0 - break + # first check if section matches + secLabelInConds = params['conds'].get('sec') + if secLabelInConds and (secLabelInConds != secLabel): + # skip to next section + break + + # then check the rest of conds + synMechConds = deepcopy(params['conds']) + synMechConds.pop('sec') + conditionsMet = sim.utils.checkConditions(synMechConds, against=synMech) if conditionsMet: # if all conditions are met, set values for this cell exclude = ['conds', 'cellConds'] + SynMechParams.reservedKeys() - for synParamName, synParamValue in { - k: v for k, v in params.items() if k not in exclude - }.items(): + paramsToModify = {k: v for k, v in params.items() if k not in exclude} + for synParamName, synParamValue in paramsToModify.items(): if sim.cfg.createPyStruct: synMech[synParamName] = synParamValue if sim.cfg.createNEURONObj: @@ -1191,59 +1151,22 @@ def modifyConns(self, params): conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # choose what to comapare to - if condKey in ['postGid']: - compareTo = self.gid - else: - compareTo = conn.get(condKey) + conds = params['conds'] - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if compareTo < condVal[0] or compareTo > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if compareTo not in condVal: - conditionsMet = 0 - break - elif compareTo != condVal: - conditionsMet = 0 - break + # `conds` may contain `postGid` key, which is deprecated in favour of having `gid` key in `postConds`, + # but for backward compatibility, try to detect it here and replace with `gid`, so checkConditions() can process it: + if 'postGid' in conds: + conds['gid'] = conds.pop('postGid') + conditionsMet = sim.utils.checkConditions(conds, against=conn, cellGid=self.gid) if conditionsMet and 'postConds' in params: - for (condKey, condVal) in params['postConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if self.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['postConds']) if conditionsMet and 'preConds' in params: try: cell = sim.net.cells[conn['preGid']] - if cell: - for (condKey, condVal) in params['preConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if cell.tags.get(condKey) < condVal[0] or cell.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if cell.tags.get(condKey) not in condVal: - conditionsMet = 0 - break - elif cell.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = cell.checkConditions(params['preConds']) except: pass # print('Warning: modifyConns() does not yet support conditions of presynaptic cells when running parallel sims') @@ -1272,34 +1195,14 @@ def modifyStims(self, params): conditionsMet = 1 if 'cellConds' in params: if conditionsMet: - for (condKey, condVal) in params['cellConds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list): - if self.tags.get(condKey) < condVal[0] or self.tags.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif self.tags.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = self.checkConditions(params['cellConds']) if conditionsMet == 1: for stim in self.stims: conditionsMet = 1 if 'conds' in params: - for (condKey, condVal) in params['conds'].items(): # check if all conditions are met - # check if conditions met - if isinstance(condVal, list) and isinstance(condVal[0], Number): - if stim.get(condKey) < condVal[0] or stim.get(condKey) > condVal[1]: - conditionsMet = 0 - break - elif isinstance(condVal, list) and isinstance(condVal[0], basestring): - if stim.get(condKey) not in condVal: - conditionsMet = 0 - break - elif stim.get(condKey) != condVal: - conditionsMet = 0 - break + conditionsMet = sim.utils.checkConditions(params['conds'], against=stim) if conditionsMet: # if all conditions are met, set values for this cell if stim['type'] == 'NetStim': # for netstims, find associated netcon diff --git a/netpyne/cell/inputs.py b/netpyne/cell/inputs.py index 07cf91716..8c525666b 100644 --- a/netpyne/cell/inputs.py +++ b/netpyne/cell/inputs.py @@ -32,14 +32,23 @@ def createRhythmicPattern(params, rand): - startStd: standard deviation of normal distrinution for start time (ms); mean is set by start param. Only used if > 0.0 - freq: oscillatory frequency of rhythmic pattern (Hz) - freqStd: standard deviation of oscillatory frequency (Hz) - - distribution: distribution type fo oscillatory frequencies; either 'normal' or 'uniform' + - distribution: distribution type for oscillatory frequencies; either 'normal' or 'uniform' - eventsPerCycle: spikes/burst per cycle; should be either 1 or 2 - repeats: number of times to repeat input pattern (equivalent to number of inputs) - stop: maximum time for last spike of pattern (ms) + - tstop: maximum time for last spike of pattern (ms) (maintained for backward compatibility) """ - + #TODO catch KeyError cases? # start is always defined start = params['start'] + if 'stop' in params: + stop = params['stop'] + elif 'tstop' in params: + stop = params['tstop'] + else: + print('stop / tstop time not defined, please provide stop time') + return np.array([]) + # If start is -1, randomize start time of inputs if start == -1: startMin = params.get('startMin', 25.0) @@ -47,7 +56,7 @@ def createRhythmicPattern(params, rand): start = rand.uniform(startMin, startMax) elif params.get('startStd', -1) > 0.0: # randomize start time based on startStd start = rand.normal(start, params['startStd']) # start time uses different prng - freq = params.get('freq', 0) + freq = params.get('freq', False) # if freq not supplied, "exit" control flow w/ print statement freqStd = params.get('freqStd', 0) eventsPerCycle = params.get('eventsPerCycle', 2) distribution = params.get('distribution', 'normal') @@ -55,12 +64,13 @@ def createRhythmicPattern(params, rand): if eventsPerCycle > 2 or eventsPerCycle <= 0: print("eventsPerCycle should be either 1 or 2, trying 2") eventsPerCycle = 2 - # If frequency is 0, create empty vector if input times - if not freq: + # If frequency is False, create empty vector if input times + if not freq: #TODO clarification, why not raise?, should error? + print("No frequency specified. Not making any alpha feeds.") t_input = [] elif distribution == 'normal': # array of mean stimulus times, starts at start - isi_array = np.arange(start, params['stop'], 1000.0 / freq) + isi_array = np.arange(start, stop, 1000.0 / freq) # array of single stimulus times -- no doublets if freqStd: # t_array = self.prng.normal(np.repeat(isi_array, self.p_ext['repeats']), stdev) @@ -86,8 +96,8 @@ def createRhythmicPattern(params, rand): t_input.sort() # Uniform Distribution elif distribution == 'uniform': - n_inputs = params['repeats'] * freq * (params['tstop'] - start) / 1000.0 - t_array = rand.uniform(start, params['tstop'], n_inputs) + n_inputs = params['repeats'] * freq * (stop - start) / 1000.0 + t_array = rand.uniform(start, stop, int(n_inputs)) if eventsPerCycle == 2: # Two arrays store doublet times t_input_low = t_array - 5 @@ -111,7 +121,7 @@ def createEvokedPattern(params, rand, inc=0): """ creates the ongoing external inputs (rhythmic) input params: - - start: time of first spike. if -1, uniform distribution between startMin and startMax (ms) + - start: time of first spike. - inc: increase in time of first spike; from cfg.inc_evinput (ms) - startStd: standard deviation of start (ms) - numspikes: total number of spikes to generate diff --git a/netpyne/cell/pointCell.py b/netpyne/cell/pointCell.py index 37216044f..86a921b5a 100644 --- a/netpyne/cell/pointCell.py +++ b/netpyne/cell/pointCell.py @@ -66,6 +66,8 @@ def __init__(self, gid, tags, create=True, associateGid=True): if 'params' in self.tags: dictParams = sim.replaceDictODict(self.tags.pop('params')) self.params = deepcopy(dictParams) + else: + self.params = {} if create and sim.cfg.createNEURONObj: self.createNEURONObj() # create cell diff --git a/netpyne/metadata/metadata.py b/netpyne/metadata/metadata.py index 5face4ea6..7527b9ae2 100644 --- a/netpyne/metadata/metadata.py +++ b/netpyne/metadata/metadata.py @@ -363,7 +363,7 @@ "suggestions": "", "help": "", "hintText": "10", - "type": "float", + "type": "func", }, "L": { "label": "Length (um)", @@ -371,7 +371,7 @@ "suggestions": "", "help": "", "hintText": "50", - "type": "float", + "type": "func", }, "Ra": { "label": "Axial resistance, Ra (ohm-cm)", @@ -379,14 +379,14 @@ "suggestions": "", "help": "", "hintText": "100", - "type": "float", + "type": "func", }, "cm": { "label": "Membrane capacitance, cm (uF/cm2)", "suggestions": "", "help": "", "hintText": "1", - "type": "float", + "type": "func", }, "pt3d": { "label": "3D points", @@ -401,7 +401,7 @@ "suggestions": "", "help": "", "hintText": "1", - "type": "float", + "type": "func", }, }, }, @@ -410,6 +410,7 @@ "help": "Dictionary of density/distributed mechanisms, including the name of the mechanism (e.g. hh or pas) and a list of properties of the mechanism (e.g. {'g': 0.003, 'e': -70}).", "suggestions": "", "hintText": "", + "type": "func", # "children": { # "hh": { # "label": "Built-in mechanism", @@ -598,28 +599,28 @@ "help": "Define the time constant for the first exponential.", "suggestions": "", "hintText": "1", - "type": "float", + "type": "func", }, "tau2": { "label": "Time constant for exponential 2 (ms)", "help": "Define the time constant for the second exponential.", "suggestions": "", "hintText": "5", - "type": "float", + "type": "func", }, "e": { "label": "Reversal potential (mV)", "help": "Reversal potential of the synaptic receptors.", "suggestions": "", "hintText": "0", - "type": "float", + "type": "func", }, "i": { "label": "synaptic current (nA)", "help": "Synaptic current in nA.", "suggestions": "", "hintText": "10", - "type": "float", + "type": "func", }, }, }, @@ -846,6 +847,218 @@ }, }, # --------------------------------------------------------------------------------------------------------------------- + # netParams.subConnParams + # --------------------------------------------------------------------------------------------------------------------- + "subConnParams": { + "label": "Sub-Cellular Connectivity parameters", + "suggestions": "", + "help": "", + "hintText": "", + "children": { + "preConds": { + "label": "Conditions for the presynaptic cells", + "help": "Presynaptic cell conditions defined using attributes/tags and the required value e.g. {'cellType': 'PYR'}. Values can be lists, e.g. {'pop': ['Exc1', 'Exc2']}. For location properties, the list values correspond to the min and max values, e.g. {'ynorm': [0.1, 0.6]}.", + "suggestions": "", + "hintText": "", + "children": { + "pop": { + "label": "Population (multiple selection available)", + "suggestions": "", + "help": "Cells belonging to this population (or list of populations) will be connected pre-synaptically.", + "hintText": "", + }, + "cellType": { + "label": "Cell type (multiple selection available)", + "suggestions": "", + "help": "Ccells with this cell type attribute/tag will be connected pre-synaptically.", + "hintText": "", + }, + "cellModel": { + "label": "Cell model (multiple selection available)", + "suggestions": "", + "help": "Cells with this cell model attribute/tag will be connected pre-synaptically.", + "hintText": "", + }, + "x": { + "label": "Range of x-axis locations", + "suggestions": "", + "help": "Cells within these x-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "y": { + "label": "Range of y-axis locations", + "suggestions": "", + "help": "Cells within these y-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "z": { + "label": "Range of z-axis locations", + "suggestions": "", + "help": "Cells within these z-axis locations will be connected pre-synaptically..", + "hintText": "", + }, + "xnorm": { + "label": "Range of normalized x-axis locations", + "suggestions": "", + "help": "Cells within these normalized x-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "ynorm": { + "label": "Range of normalized y-axis locations", + "suggestions": "", + "help": "Cells within these normalized y-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + "znorm": { + "label": "Range of normalized z-axis locations", + "suggestions": "", + "help": "Cells within these normalized z-axis locations will be connected pre-synaptically.", + "hintText": "", + }, + }, + }, + "postConds": { + "label": "Conditions for the postsynaptic cells", + "help": "Defined as a dictionary with the attributes/tags of the postsynaptic cell and the required values e.g. {'cellType': 'PYR'}. Values can be lists, e.g. {'pop': ['Exc1', 'Exc2']}. For location properties, the list values correspond to the min and max values, e.g. {'ynorm': [0.1, 0.6]}.", + "suggestions": "", + "hintText": "", + "children": { + "pop": { + "label": "Population (multiple selection available)", + "suggestions": "", + "help": "Cells belonging to this population (or list of populations) will be connected post-synaptically.", + "hintText": "", + }, + "cellType": { + "label": "Cell type (multiple selection available)", + "suggestions": "", + "help": "Ccells with this cell type attribute/tag will be connected post-synaptically.", + "hintText": "", + }, + "cellModel": { + "label": "Cell model (multiple selection available)", + "suggestions": "", + "help": "Cells with this cell model attribute/tag will be connected post-synaptically.", + "hintText": "", + }, + "x": { + "label": "Range of x-axis locations", + "suggestions": "", + "help": "Cells within these x-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "y": { + "label": "Range of y-axis locations", + "suggestions": "", + "help": "Cells within these y-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "z": { + "label": "Range of z-axis locations", + "suggestions": "", + "help": "Cells within these z-axis locations will be connected post-synaptically..", + "hintText": "", + }, + "xnorm": { + "label": "Range of normalized x-axis locations", + "suggestions": "", + "help": "Cells within these normalized x-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "ynorm": { + "label": "Range of normalized y-axis locations", + "suggestions": "", + "help": "Cells within these normalized y-axis locations will be connected post-synaptically.", + "hintText": "", + }, + "znorm": { + "label": "Range of normalized z-axis locations", + "suggestions": "", + "help": "Cells within these normalized z-axis locations will be connected post-synaptically.", + "hintText": "", + }, + }, + }, + "groupSynMechs": { + "label": "Synaptic mechanism", + "help": " List of synaptic mechanisms grouped together when redistributing synapses. If omitted, post-synaptic locations of all connections (meeting preConds and postConds) are redistributed independently with a given profile (defined below). If a list is provided, synapses of common connections (same pre- and post-synaptic neurons for each mechanism) are relocated to the same location. For example, [‘AMPA’,’NMDA’].", + "suggestions": "", + "hintText": "", + }, + "sec": { + "label": "Redistributed Synapse Sections", + "help": "List of sections admitting redistributed synapses. If omitted, the section used to redistribute synapses is the soma or, if it does not exist, the first available section in the post-synaptic cell. For example, [‘Adend1’,’Adend2’, ‘Adend3’,’Bdend’].", + "suggestions": "", + "hintText": "soma", + "type": "list(str)", + }, + "density": { + "label": "Type of redistribution", + "help": "", + "suggestions": "", + "hintText": "", + "children": { + "gridY": { + "label": "Depth y-coordinate Positions", + "help": "List of positions in y-coordinate (depth).", + "suggestions": "", + "hintText": "", + "type": "list(int, float)", + }, + "gridX": { + "label": "Depth x-coordinate Positions", + "help": "List of positions in x-coordinate (or z).", + "suggestions": "", + "hintText": "", + "type": "list(int, float)", + }, + "fixedSomaY": { + "label": "Soma's y-coordinate", + "help": "Absolute position y-coordinate of the soma, used to shift gridY (also provided in absolute coordinates).", + "suggestions": "", + "hintText": "", + "type": "int,float", + }, + "gridValues": { + "label": "Synaptic Density", + "help": "One or two-dimensional list expressing the (relative) synaptic density in the coordinates defined by (gridX and) gridY.", + "suggestions": "", + "hintText": "", + "type": "list(int, float)", + }, + "ref_sec": { + "label": "Reference Section", + "help": "Section used as a reference from which distance is computed. If omitted, the section used to reference distances is the soma or, if it does not exist, anything starting with ‘som’ or, otherwise, the first available section in the post-synaptic cell.", + "suggestions": "", + "hintText": "", + "type": "str", + }, + "ref_seg": { + "label": "Reference Segment", + "help": "Segment within the section used to reference the distances. If omitted, it is used a default value (0.5).", + "suggestions": "", + "hintText": "", + "type": "int, float", + }, + "target_distance": { + "label": "Target Distance", + "help": "Target distance from the reference where synapses will be reallocated. If omitted, this value is set to 0. The chosen location will be the closest to this target, between the allowed sections.", + "suggestions": "", + "hintText": "", + "type": "int, float", + }, + "coord": { + "label": "Coordinate's System", + "help": "Coordinates’ system used to compute distance. If omitted, the distance is computed along the dendritic tree. Alternatively, it may be used ‘cartesian’ to calculate the distance in the euclidean space (distance from the reference to the target segment in the cartesian coordinate system).", + "suggestions": "", + "hintText": "", + "type": "int, float", + }, + } + } + }, + }, + # --------------------------------------------------------------------------------------------------------------------- # netParams.stimSourceParams # --------------------------------------------------------------------------------------------------------------------- "stimSourceParams": { diff --git a/netpyne/network/pop.py b/netpyne/network/pop.py index ce19b64a4..f34a2e75f 100644 --- a/netpyne/network/pop.py +++ b/netpyne/network/pop.py @@ -21,6 +21,7 @@ from numpy import pi, sqrt, sin, cos, arccos import numpy as np from neuron import h # Import NEURON +from netpyne import sim ############################################################################### @@ -39,7 +40,11 @@ def __init__(self, label, tags): self.tags = tags # list of tags/attributes of population (eg. numCells, cellModel,...) self.tags['pop'] = label self.cellGids = [] # list of cell gids beloging to this pop + self._setCellClass() # set type of cell + if self.cellModelClass == sim.PointCell: + self.__handlePointCellParams() + self.rand = h.Random() # random number generator def _distributeCells(self, numCellsPop): @@ -47,8 +52,6 @@ def _distributeCells(self, numCellsPop): Distribute cells across compute nodes using round-robin """ - from .. import sim - hostCells = {} for i in range(sim.nhosts): hostCells[i] = [] @@ -106,8 +109,6 @@ def createCellsFixedNum(self): Create population cells based on fixed number of cells """ - from .. import sim - cells = [] self.rand.Random123(self.tags['numCells'], sim.net.lastGid, sim.cfg.seeds['loc']) self.rand.uniform(0, 1) @@ -207,8 +208,6 @@ def createCellsDensity(self): Create population cells based on density """ - from .. import sim - cells = [] shape = sim.net.params.shape sizeX = sim.net.params.sizeX @@ -372,8 +371,6 @@ def createCellsList(self): Create population cells based on list of individual cells """ - from .. import sim - cells = [] self.tags['numCells'] = len(self.tags['cellsList']) for i in self._distributeCells(len(self.tags['cellsList']))[sim.rank]: @@ -411,8 +408,6 @@ def createCellsGrid(self): Create population cells based on fixed number of cells """ - from .. import sim - cells = [] rangeLocs = [[0, getattr(sim.net.params, 'size' + coord)] for coord in ['X', 'Y', 'Z']] @@ -462,7 +457,6 @@ def createCellsGrid(self): return cells def _createCellTags(self): - from .. import sim # copy all pop tags to cell tags, except those that are pop-specific cellTags = {k: v for (k, v) in self.tags.items() if k in sim.net.params.popTagsCopiedToCells} @@ -474,20 +468,6 @@ def _setCellClass(self): Set cell class (CompartCell, PointCell, etc) """ - from .. import sim - - # obtain cellModel either from cellParams or popParams - if ( - 'cellType' in self.tags - and self.tags['cellType'] in sim.net.params.cellParams - and 'cellModel' in sim.net.params.cellParams[self.tags['cellType']] - ): - cellModel = sim.net.params.cellParams[self.tags['cellType']]['cellModel'] - elif 'cellModel' in self.tags: - cellModel = self.tags['cellModel'] - else: - cellModel = None - # Check whether it's a NeuroML2 based cell # ! needs updating to read cellModel info from cellParams if 'originalFormat' in self.tags: @@ -495,64 +475,77 @@ def _setCellClass(self): self.cellModelClass = sim.NML2Cell if self.tags['originalFormat'] == 'NeuroML2_SpikeSource': self.cellModelClass = sim.NML2SpikeSource - else: + # obtain cellModel either from popParams.. + cellModel = self.tags.get('cellModel') + + if cellModel is None: + # .. or from cellParams + cellType = self.tags.get('cellType') + if cellType: + cellRule = sim.net.params.cellParams.get(cellType, {}) + cellModel = cellRule.get('cellModel') + else: + # TODO: or throw error? + pass + # set cell class: CompartCell for compartmental cells of PointCell for point neurons (NetStims, IntFire1,...) - try: # check if cellModel corresponds to an existing point process mechanism; if so, use PointCell - tmp = getattr(h, cellModel) + if cellModel and hasattr(h, cellModel): + # check if cellModel corresponds to an existing point process mechanism; if so, use PointCell self.cellModelClass = sim.PointCell - excludeTags = [ - 'pop', - 'cellModel', - 'cellType', - 'numCells', - 'density', - 'cellsList', - 'gridSpacing', - 'xRange', - 'yRange', - 'zRange', - 'xnormRange', - 'ynormRange', - 'znormRange', - 'vref', - 'spkTimes', - 'dynamicRates', - ] - params = {k: v for k, v in self.tags.items() if k not in excludeTags} - self.tags['params'] = params - for k in self.tags['params']: - self.tags.pop(k) - sim.net.params.popTagsCopiedToCells.append('params') - - # if point cell params defined directly in pop params, need to scan them for string functions - if len(params): - from ..specs.netParams import CellParams - - CellParams.updateStringFuncsWithPopParams(self.tags['pop'], params) - except: - if getattr(self.tags, 'cellModel', None) in [ - 'NetStim', - 'DynamicNetStim', - 'VecStim', - 'IntFire1', - 'IntFire2', - 'IntFire4', - ]: + else: + # otherwise assume has sections and some cellParam rules apply to it; use CompartCell + self.cellModelClass = sim.CompartCell + # if model is known but wasn't recognized, issue warning + knownPointps = ['NetStim', 'DynamicNetStim', 'VecStim', 'IntFire1', 'IntFire2', 'IntFire4'] + if getattr(self.tags, 'cellModel', None) in knownPointps: print( 'Warning: could not find %s point process mechanism required for population %s' % (cellModel, self.tags['pop']) ) - self.cellModelClass = ( - sim.CompartCell - ) # otherwise assume has sections and some cellParam rules apply to it; use CompartCell + + def __handlePointCellParams(self): + + if 'params' in self.tags and isinstance(self.tags['params'], dict): + # in some cases, params for point cell may already be grouped in the nested 'params' dict. + params = self.tags['params'] + else: + # otherwise, try extracting them from the top level of tags dict + excludeTags = [ + 'pop', + 'cellModel', + 'cellType', + 'numCells', + 'density', + 'cellsList', + 'gridSpacing', + 'xRange', + 'yRange', + 'zRange', + 'xnormRange', + 'ynormRange', + 'znormRange', + 'vref', + 'spkTimes', + 'dynamicRates', + ] + params = {k: v for k, v in self.tags.items() if k not in excludeTags} + self.tags['params'] = params + for k in self.tags['params']: + self.tags.pop(k) + sim.net.params.popTagsCopiedToCells.append('params') + + # if point cell params defined directly in pop params, need to scan them for string functions + if len(params): + from ..specs.netParams import CellParams + + CellParams.updateStringFuncsWithPopParams(self.tags['pop'], params) + def calcRelativeSegCoords(self): """Calculate segment coordinates from 3d point coordinates Used for LFP calc (one per population cell; assumes same morphology)""" - from .. import sim - localPopGids = list(set(sim.net.gid2lid.keys()).intersection(set(self.cellGids))) if localPopGids: cell = sim.net.cells[sim.net.gid2lid[localPopGids[0]]] @@ -621,8 +614,6 @@ def calcRelativeSegCoords(self): def __getstate__(self): """Removes non-picklable h objects so can be pickled and sent via py_alltoall""" - from .. import sim - odict = self.__dict__.copy() # copy the dict since we change it odict = sim.replaceFuncObj(odict) # replace h objects with None so can be pickled # odict['cellModelClass'] = str(odict['cellModelClass']) diff --git a/netpyne/plotting/plotRaster.py b/netpyne/plotting/plotRaster.py index 9d37eb8f4..a5be18c9a 100644 --- a/netpyne/plotting/plotRaster.py +++ b/netpyne/plotting/plotRaster.py @@ -18,6 +18,7 @@ def plotRaster( popLabels=None, popColors=None, syncLines=False, + colorbyPhase = None, legend=True, colorList=None, orderInverse=False, @@ -111,6 +112,27 @@ def plotRaster( *Default:* ``False`` + colorbyPhase : dict + Dictionary specifying conditions to plot spikes colored by the phase of a simultaneous signal, filtered in a given range + + *Default:* ``None`` colors spikes according to other options (by populations) + + *Dictionary entries:* + + ``'signal'`` specifies the signal. Options are: ``'LFP'``, which takes the signal from the local fiel potential generated in the ongoing simulation, a numpy array of scalars (for example, an external signal used for stimulation), or an external pickle file, + + ``'fs'`` is the sampling frequency, which should be specified when the signal is obtained from external sources (pickle file or numpy array). Otherwise, it is assumed to be 1000 Hz. If the signal is specified by ``'LFP'``, then the sampling rate is obtained from the internal simulation (cfg.recordStep), + + ``'electrode'`` selects the electrode from the LFP setup. Default is electrode 1, + + ``'filtFreq'`` is a list specifying the range for filtering the signal (band-pass). For example, ``[4,8]`` to select theta rhythm. The default is a very broadband filtering (essentially, the raw signal) ``[1,500]``, + + ``'filtOrder'`` is the filter order (Butterworth) to process the signal, + + ``'pop_background'`` is a boolean option to color each population alternately with a gray background, for better visualization. The default is False, + + ``'include_signal'`` is a boolean option to plot the filtered signal below the raster plot. The default is False. + legend : bool Whether or not to add a legend to the plot. @@ -193,7 +215,8 @@ def plotRaster( sim = kwargs['sim'] rasterData = sim.analysis.prepareRaster( - timeRange=timeRange, maxSpikes=maxSpikes, orderBy=orderBy, popRates=popRates, **kwargs + timeRange=timeRange, maxSpikes=maxSpikes, orderBy=orderBy, + popRates=popRates, colorbyPhase=colorbyPhase, **kwargs ) print('Plotting raster...') @@ -208,6 +231,8 @@ def plotRaster( spkTimes = rasterData['spkTimes'] spkInds = rasterData['spkInds'] spkGids = rasterData['spkGids'] + if colorbyPhase: + spkPhases = rasterData['spkPhases'] if not popNumCells: popNumCells = rasterData.get('popNumCells') @@ -288,6 +313,16 @@ def plotRaster( else: timeRange = [0, np.ceil(max(spkTimes))] + # Set features for raster plot colored by phase + if colorbyPhase: + spkColors = spkPhases + legend = False + kwargs['colorbar'] = {'vmin': -180, 'vmax': 180} + kwargs['background'] = False + if 'pop_background' in colorbyPhase: + if colorbyPhase['pop_background'] == True: + kwargs['background'] = {'popLabels': popLabels, 'popNumCells': popNumCells, 'timeRange': timeRange} + # Create a dictionary with the inputs for a scatter plot scatterData = {} scatterData['x'] = spkTimes @@ -297,10 +332,16 @@ def plotRaster( scatterData['marker'] = '|' scatterData['markersize'] = 5 scatterData['linewidth'] = 2 - scatterData['cmap'] = None scatterData['norm'] = None scatterData['alpha'] = None scatterData['linewidths'] = None + scatterData['cmap'] = None + if colorbyPhase: + scatterData['cmap'] = 'hsv' + if 'include_signal' in colorbyPhase and colorbyPhase['include_signal']==True: + scatterData['time'] = rasterData.get('time') + scatterData['signal'] = rasterData.get('signal') + scatterData['timeRange'] = timeRange # If a kwarg matches a scatter input key, use the kwarg value instead of the default for kwarg in list(kwargs.keys()): @@ -326,7 +367,10 @@ def plotRaster( kwargs.pop(kwarg) # create Plotter object - rasterPlotter = ScatterPlotter(data=scatterData, kind='raster', axis=axis, **axisArgs, **kwargs) + if 'signal' in scatterData: + rasterPlotter = ScatterPlotter(data=scatterData, kind='raster&signal', axis=axis, **axisArgs, **kwargs) + else: + rasterPlotter = ScatterPlotter(data=scatterData, kind='raster', axis=axis, **axisArgs, **kwargs) metaFig = rasterPlotter.metafig # add spike lines diff --git a/netpyne/plotting/plotter.py b/netpyne/plotting/plotter.py index 82c45b501..be967d265 100644 --- a/netpyne/plotting/plotter.py +++ b/netpyne/plotting/plotter.py @@ -127,13 +127,26 @@ def __init__(self, kind, sim=None, subplots=None, sharex=False, sharey=False, au else: dpi = self.rcParams['figure.dpi'] + if 'constrained_layout' in kwargs: + constrained_layout = kwargs['constrained_layout'] + else: + constrained_layout = False + if autosize: maxplots = np.max([nrows, ncols]) figSize0 = figSize[0] + (maxplots - 1) * (figSize[0] * autosize) figSize1 = figSize[1] + (maxplots - 1) * (figSize[1] * autosize) figSize = [figSize0, figSize1] - self.fig, self.ax = plt.subplots(nrows, ncols, sharex=sharex, sharey=sharey, figsize=figSize, dpi=dpi) + gridspec_kw = None +# if 'height_ratios' in kwargs: +# gridspec_kw = {'height_ratios': kwargs['height_ratios'], 'right': 0.3} + if 'gridspec_kw' in kwargs: + gridspec_kw = kwargs['gridspec_kw'] + + self.fig, self.ax = plt.subplots(nrows, ncols, sharex=sharex, sharey=sharey, + figsize=figSize, dpi=dpi, gridspec_kw=gridspec_kw, + constrained_layout=constrained_layout) # Add a metafig attribute to the figure self.fig.metafig = self @@ -205,6 +218,9 @@ def saveFig(self, sim=None, fileName=None, fileDesc=True, fileType='png', fileDi if '.' in saveFig: fileName, fileType = os.path.splitext(saveFig) fileType = fileType[1:] # drop the dot + elif saveFig == 'movie': + from neuron import h + fileName = sim.cfg.filename + '_shape_movie_' + str(round(h.t, 1)) + '.png' else: fileName = saveFig @@ -490,7 +506,16 @@ def __init__(self, data, kind, axis=None, twinx=False, twiny=False, sim=None, me # If an axis is input, plot there; otherwise make a new figure and axis if self.axis is None: if self.metafig is None: - self.metafig = MetaFigure(kind=self.kind, **kwargs) + if self.kind == 'raster&signal': + kwargs.update({'constrained_layout': True}) + kwargs.update({'gridspec_kw': {'height_ratios': [2,1], + 'right': 0.2}}) + # 'left':0.2, + # 'top':0.3, + # 'bottom':0.2}}) + self.metafig = MetaFigure(kind=self.kind, subplots=2, sharex=True, **kwargs) + else: + self.metafig = MetaFigure(kind=self.kind, **kwargs) self.fig = self.metafig.fig self.axis = self.metafig.ax else: @@ -561,11 +586,14 @@ def saveData(self, fileName=None, fileDesc=None, fileType=None, fileDir=None, si self.data, fileName=fileName, fileDesc=fileDesc, fileType=fileType, fileDir=fileDir, sim=sim, **kwargs ) - def formatAxis(self, **kwargs): + def formatAxis(self, axis=None, **kwargs): """Method to format the axis Parameters ---------- + axis : None or object + the axis to format + title : str Title to add to the axis. @@ -589,33 +617,39 @@ def formatAxis(self, **kwargs): Whether to invert the y axis. """ + curAx = axis + if curAx==None: + curAx = self.axis if 'title' in kwargs: - self.axis.set_title(kwargs['title']) + curAx.set_title(kwargs['title']) if 'xlabel' in kwargs: - self.axis.set_xlabel(kwargs['xlabel']) + curAx.set_xlabel(kwargs['xlabel']) if 'ylabel' in kwargs: - self.axis.set_ylabel(kwargs['ylabel']) + curAx.set_ylabel(kwargs['ylabel']) if 'xlim' in kwargs: if kwargs['xlim'] is not None: - self.axis.set_xlim(kwargs['xlim']) + curAx.set_xlim(kwargs['xlim']) if 'ylim' in kwargs: if kwargs['ylim'] is not None: - self.axis.set_ylim(kwargs['ylim']) + curAx.set_ylim(kwargs['ylim']) if 'invert_yaxis' in kwargs: if kwargs['invert_yaxis'] is True: - self.axis.invert_yaxis() + curAx.invert_yaxis() def addLegend(self, handles=None, labels=None, **kwargs): """Method to add a legend to the axis Parameters ---------- + axis : None or object + the axis to add the legends + handles : list List of Matplotlib legend handles. @@ -686,6 +720,7 @@ def addLegend(self, handles=None, labels=None, **kwargs): def addScalebar( self, + axis=None, matchx=True, matchy=True, hidex=True, @@ -703,6 +738,9 @@ def addScalebar( Parameters ---------- + axis : None or object + the axis to add the scalebar + matchx : bool If True, set size of scale bar to spacing between ticks, if False, set size using sizex params. @@ -766,8 +804,12 @@ def addScalebar( """ + curAx = axis + if curAx==None: + curAx = self.axis + _add_scalebar( - self.axis, + curAx, matchx=matchx, matchy=matchy, hidex=hidex, @@ -782,17 +824,67 @@ def addScalebar( **kwargs ) - def addColorbar(self, **kwargs): + def addColorbar(self, axis=None, **kwargs): """Method to add a color bar to the axis Parameters ---------- + axis : None or object + the axis to add the colorbar + kwargs : str You can enter any Matplotlib colorbar parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.colorbar.html """ - plt.colorbar(mappable=self.axis.get_images()[0], ax=self.axis, **kwargs) - def finishAxis(self, **kwargs): + curAx = axis + if curAx==None: + curAx = self.axis + + if 'vmin' in kwargs.keys(): + vmin = kwargs['vmin'] + kwargs.pop('vmin') + if 'vmax' in kwargs.keys(): + vmax = kwargs['vmax'] + kwargs.pop('vmax') + if 'vmin' in locals() and 'vmax' in locals(): + cbar = plt.colorbar(mappable=mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(vmin=vmin, vmax=vmax), cmap=self.cmap), + ax=curAx, ticks=[vmin, vmin/2, 0, vmax/2, vmax], **kwargs) + cbar.set_label('Phase', rotation=270) + else: + plt.colorbar(mappable=self.axis.get_images()[0], ax=curAx, **kwargs) + + def addBackground(self, axis=None, **kwargs): + """Method to add striped gray background to populations - used in plotRaster with "colorbyPhase" + + Parameters + ---------- + axis : None or object + the axis to add the background + + kwargs : str + You can enter any Matplotlib colorbar parameter as a kwarg. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.colorbar.html + """ + from matplotlib.pyplot import yticks, barh + + curAx = axis + if curAx==None: + curAx = self.axis + + if 'popNumCells' in kwargs.keys(): + popSizes = kwargs['popNumCells'] + yTicksPos = [] + accum = 0 + for i, size in enumerate(popSizes): + yTicksPos.append(accum + popSizes[i] / 2) # yCenter of pop + accum += popSizes[i] + curAx.set_yticks(np.array(yTicksPos)) + curAx.set_yticklabels(kwargs['popLabels']) + curAx.set_xlim(kwargs['timeRange']) + curAx.set_ylim([0,accum]) + curAx.barh(yTicksPos, left=kwargs['timeRange'][0], width=kwargs['timeRange'][1]-kwargs['timeRange'][0]-1, + height=popSizes, align='center', color=['#E3E3E3', 'w'], alpha=0.5) + + def finishAxis(self, axis=None, **kwargs): """Method to finalize an axis Parameters @@ -819,7 +911,11 @@ def finishAxis(self, **kwargs): """ - self.formatAxis(**kwargs) + curAx = axis + if curAx==None: + curAx = self.axis + + self.formatAxis(axis=curAx, **kwargs) if 'saveData' in kwargs: if kwargs['saveData']: @@ -827,28 +923,33 @@ def finishAxis(self, **kwargs): if 'legend' in kwargs: if kwargs['legend'] is True: - self.addLegend(**kwargs) + self.addLegend(axis=curAx,**kwargs) elif type(kwargs['legend']) == dict: - self.addLegend(**kwargs['legend']) + self.addLegend(axis=curAx,**kwargs['legend']) if 'scalebar' in kwargs: if kwargs['scalebar'] is True: - self.addScalebar() + self.addScalebar(axis=curAx) elif type(kwargs['scalebar']) == dict: - self.addScalebar(**kwargs['scalebar']) + self.addScalebar(axis=curAx,**kwargs['scalebar']) if 'colorbar' in kwargs: if kwargs['colorbar'] is True: - self.addColorbar() + self.addColorbar(axis=curAx) elif type(kwargs['colorbar']) == dict: - self.addColorbar(**kwargs['colorbar']) + self.addColorbar(axis=curAx, **kwargs['colorbar']) if 'grid' in kwargs: - self.axis.minorticks_on() + curAx.minorticks_on() if kwargs['grid'] is True: - self.axis.grid() + curAx.grid() elif type(kwargs['grid']) == dict: - self.axis.grid(**kwargs['grid']) + curAx.grid(**kwargs['grid']) + + if 'background' in kwargs: + if type(kwargs['background']) == dict: + self.addBackground(axis=curAx, **kwargs['background']) + # If this is the only axis on the figure, finish the figure if (type(self.metafig.ax) != np.ndarray) and (type(self.metafig.ax) != list): @@ -866,6 +967,11 @@ def __init__(self, data, axis=None, **kwargs): super().__init__(data=data, axis=axis, **kwargs) self.kind = 'scatter' + if 'signal' in data: + self.kind = 'scatter&signal' + self.time = data.get('time') + self.signal = data.get('signal') + self.timeRange = data.get('timeRange') self.x = data.get('x') self.y = data.get('y') self.s = data.get('s') @@ -879,20 +985,46 @@ def __init__(self, data, axis=None, **kwargs): def plot(self, **kwargs): - scatterPlot = self.axis.scatter( - x=self.x, - y=self.y, - s=self.s, - c=self.c, - marker=self.marker, - linewidth=self.linewidth, - cmap=self.cmap, - norm=self.norm, - alpha=self.alpha, - linewidths=self.linewidths, - ) + if self.kind=='scatter': + scatterPlot = self.axis.scatter( + x=self.x, + y=self.y, + s=self.s, + c=self.c, + marker=self.marker, + linewidth=self.linewidth, + cmap=self.cmap, + norm=self.norm, + alpha=self.alpha, + linewidths=self.linewidths, + ) + self.finishAxis(**kwargs) + + elif self.kind=='scatter&signal': + scatterPlot = self.axis[0].scatter( + x=self.x, + y=self.y, + s=self.s, + c=self.c, + marker=self.marker, + linewidth=self.linewidth, + cmap=self.cmap, + norm=self.norm, + alpha=self.alpha, + linewidths=self.linewidths, + ) + + linePlot = self.axis[1].plot( + self.time, + self.signal, + ) + self.finishAxis(axis=self.axis[0],**kwargs) + self.axis[0].set_xlabel('') + self.axis[0].set_ylabel('Cells (grouped by populations)') + self.axis[1].set_xlabel('Time (ms)') + self.axis[1].set_ylabel('Filtered signal') + self.metafig.finishFig(**kwargs) - self.finishAxis(**kwargs) return self.fig diff --git a/netpyne/sim/setup.py b/netpyne/sim/setup.py index ab0d272c8..ea7879f2f 100644 --- a/netpyne/sim/setup.py +++ b/netpyne/sim/setup.py @@ -94,8 +94,6 @@ def initialize(netParams=None, simConfig=None, net=None): sim.setNet(sim.Network()) # or create new network sim.setNetParams(netParams) # set network parameters - sim.net.params.synMechParams.preprocessStringFunctions() - sim.net.params.cellParams.preprocessStringFunctions() if sim.nhosts > 1: sim.cfg.validateNetParams = False # turn of error chceking if using multiple cores @@ -107,8 +105,9 @@ def initialize(netParams=None, simConfig=None, net=None): valid, failed = validator.validateNetParams(netParams) sim.timing('stop', 'validationTime') if failed: - failed = map(lambda entry: entry[0], failed) # get failed component name - print(f"\nNetParams validation identified some potential issues in {', '.join(failed)}. See above for details.") + failedComps = [err.component for err in failed] # get failed component name + failedComps = list(set(failedComps)) # keep unique elements only + print(f"\nNetParams validation identified some potential issues in {', '.join(failedComps)}. See above for details.") else: print("\nNetParams validation successful.") except Exception as e: @@ -172,6 +171,9 @@ def setNetParams(params): # set mapping from netParams variables to cfg (used in batch) sim.net.params.setCfgMapping(sim.cfg) + sim.net.params.cellParams.preprocessStringFunctions() + sim.net.params.synMechParams.preprocessStringFunctions() + # ------------------------------------------------------------------------------ # Set simulation config @@ -453,7 +455,7 @@ def setupRecording(): # stim spike recording if 'plotRaster' in sim.cfg.analysis: if isinstance(sim.cfg.analysis['plotRaster'], dict) and 'include' in sim.cfg.analysis['plotRaster']: - netStimLabels = list(sim.net.params.stimSourceParams.keys()) + ['allNetStims'] + netStimLabels = list(sim.net.params.stimSourceParams.keys()) + ['allNetStims'] + ['all'] for item in sim.cfg.analysis['plotRaster']['include']: if item in netStimLabels: sim.cfg.recordStim = True diff --git a/netpyne/sim/utils.py b/netpyne/sim/utils.py index 6bc0a37a9..e408bfec8 100644 --- a/netpyne/sim/utils.py +++ b/netpyne/sim/utils.py @@ -1017,6 +1017,43 @@ def clearAll(): gc.collect() +def checkConditions(conditions, against, cellGid=None): + + conditionsMet = 1 + for (condKey, condVal) in conditions.items(): + + # gid matching will be processed in specific way + gidCompare = False + if (cellGid is not None) and (condKey == 'gid'): + compareTo = cellGid + gidCompare = True + + else: + compareTo = against.get(condKey) + + if isinstance(condVal, list): + if isinstance(condVal[0], Number): + if gidCompare: + if compareTo not in condVal: + conditionsMet = 0 + break + elif compareTo < condVal[0] or compareTo > condVal[1]: + conditionsMet = 0 + break + elif isinstance(condVal[0], basestring): + if compareTo not in condVal: + conditionsMet = 0 + break + elif isinstance(compareTo, list): # e.g. to match 'label', which may be list + if condVal not in compareTo: + conditionsMet = 0 + break + elif compareTo != condVal: + conditionsMet = 0 + break + return conditionsMet + + # ------------------------------------------------------------------------------ # Create a subclass of json.JSONEncoder to convert numpy types in Python types # ------------------------------------------------------------------------------ diff --git a/netpyne/sim/validate.py b/netpyne/sim/validate.py deleted file mode 100644 index 45752a663..000000000 --- a/netpyne/sim/validate.py +++ /dev/null @@ -1,169 +0,0 @@ -from schema import Schema, Optional, And, Or, Use -from collections import ChainMap - -cell_spec = { - str: { - 'secs': { - Optional(And(str, Use(str.lower), lambda s: s in ['soma', 'dend'])): { - Optional('geom'): { - Optional('L'): Or(int, float), - Optional('Ra'): Or(int, float), - Optional('diam'): Or(int, float), - Optional('cm'): Or(int, float), - Optional('pt3d'): And( - [(float, float, float, float)], lambda t: len(list(filter(lambda x: len(x) != 4, t))) == 0 - ), - }, - Optional('mechs'): { - And(str, Use(str.lower), lambda s: s in ['hh', 'pas']): { - Optional('el'): int, - Optional('gkbar'): float, - Optional('gl'): float, - Optional('gnabar'): float, - Optional('g'): float, - Optional('e'): Or(int, float), - } - }, - Optional('topol'): { - Optional('parentSec'): And(str, Use(str.lower), lambda s: s in ['soma', 'dend']), - Optional('childX'): Or(int, float), - Optional('parentX'): Or(int, float), - }, - Optional('pointps'): { - str: { - 'mod': str, - 'C': Or(int, float), - 'k': Or(int, float), - 'vr': Or(int, float), - 'vt': Or(int, float), - 'vpeak': Or(int, float), - 'a': Or(int, float), - 'b': Or(int, float), - 'c': Or(int, float), - 'd': Or(int, float), - 'celltype': int, - } - }, - } - } - } -} - -population_spec = { - str: { - 'cellType': str, - 'numCells': int, - Optional('yRange'): [int], - Optional('ynormRange'): [float], - Optional('cellModel'): str, - } -} - - -synaptic_spec = { - str: { - 'mod': And(str, Use(str.lower), lambda s: s in ['exp2syn']), - 'tau1': Or(int, float), - 'tau2': Or(int, float), - 'e': Or(int, float), - } -} - - -stimulation_source_spec = { - str: { - Optional('type'): And(str, Use(str.lower), lambda s: s in ['iclamp', 'vclamp', 'alphasynapse', 'netstim']), - Optional('rate'): int, - Optional('noise'): float, - Optional('del'): int, - Optional('dur'): Or(int, [int]), - Optional('amp'): Or(str, [int]), - Optional('gain'): float, - Optional('tau1'): Or(int, float), - Optional('tau2'): Or(int, float), - Optional('rstim'): Or(int, float), - Optional('e'): Or(int, float), - Optional('gmax'): str, - Optional('onset'): str, - Optional('tau'): Or(int, float), - Optional('interval'): str, - Optional('start'): Or(int, float), - } -} - - -stimulation_target_spec = { - str: { - Optional('source'): str, - Optional('conds'): { - Optional('cellType'): Or(str, [str]), - Optional('cellList'): [Or(int, float)], - Optional('pop'): str, - Optional('ynorm'): [Or(int, float)], - }, - Optional('weight'): Or( - float, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('delay'): Or( - int, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('synMech'): str, - Optional('loc'): float, - Optional('sec'): str, - } -} - - -connection_spec = { - str: { - Optional('preConds'): { - Optional('pop'): Or(str, [str]), - Optional('y'): [Or(int, float)], - Optional('cellType'): str, - }, - Optional('postConds'): { - Optional('pop'): Or(str, [str]), - Optional('y'): [Or(int, float)], - Optional('cellType'): str, - }, - Optional(And(str, Use(str.lower), lambda s: s in ['probability', 'convergence', 'divergence'])): Or( - float, str - ), - Optional('weight'): Or( - float, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('delay'): Or( - int, str - ), # The string is for capturing functions. May want to validate it's valid python - Optional('synMech'): str, - Optional('loc'): float, - Optional('sec'): And(str, Use(str.lower), lambda s: s in ['dend']), - } -} - - -cell_schema = Schema(cell_spec) -population_schema = Schema(population_spec) -synaptic_schema = Schema(synaptic_spec) -stimulation_source_schema = Schema(stimulation_source_spec) -stimulation_target_schema = Schema(stimulation_target_spec) -connection_schema = Schema(connection_spec) - -net_param_schema = Schema( - dict( - ChainMap( - *[ - cell_spec, - population_spec, - synaptic_spec, - stimulation_source_spec, - stimulation_target_spec, - connection_spec, - ] - ) - ) -) - - -def check_netparams(net_params: dict): - cell_schema.validate(net_params.cellParam) diff --git a/netpyne/sim/validator.py b/netpyne/sim/validator.py index 1350a8109..07788e85e 100644 --- a/netpyne/sim/validator.py +++ b/netpyne/sim/validator.py @@ -17,6 +17,8 @@ def __init__(self, netParams): self.validateModels = True # cfg.validateNetParamsMechs +numberOrStringFunc = Or(int, float, str, error='Expected number (int, float) or a function as string.') + def general_specs(): specs = { '_labelid': int, @@ -64,6 +66,7 @@ def pop_specs(context): specs = { str: { + # TODO: either cellType or cellModel has to be present?? Optional('cellType'): And( str, lambda s: __isKeyIn(s, context.cellParams) or __isAmongConds(s, 'cellType', context.cellParams) @@ -92,7 +95,7 @@ def pop_specs(context): } ], Optional('numCells'): Or(int, float), - Optional('density'): Or(int, float, str), # string-based function is allowed + Optional('density'): numberOrStringFunc, # string-based function is allowed Optional('gridSpacing'): Or(And([Or(int, float)], lambda s: len(s) == 3), Or(int, float)), Optional('xRange'): And([Or(int, float)], lambda s: len(s) == 2), Optional('yRange'): And([Or(int, float)], lambda s: len(s) == 2), @@ -208,11 +211,11 @@ def cell_specs(context): Optional('secs'): { ## It is optional because it may NOT be a compartCell, but for compartCells this entry is mandatory str: { Optional('geom'): { - Optional('diam'): Or(int, float, str), - Optional('L'): Or(int, float, str), - Optional('Ra'): Or(int, float, str), - Optional('cm'): Or(int, float, str), - Optional('nseg'): Or(int, float, str), + Optional('diam'): numberOrStringFunc, + Optional('L'): numberOrStringFunc, + Optional('Ra'): numberOrStringFunc, + Optional('cm'): numberOrStringFunc, + Optional('nseg'): numberOrStringFunc, Optional('pt3d'): [ And( lambda s: len(s) == 4, # list of (list or tuples), each with 4 components @@ -233,14 +236,14 @@ def cell_specs(context): ), Optional('mechs'): { Optional('hh'): { # one possible built-in mechanism, very used - Optional('gnabar'): Or(int, float, str), - Optional('gkbar'): Or(int, float, str), - Optional('gl'): Or(int, float, str), - Optional('el'): Or(int, float, str), + Optional('gnabar'): numberOrStringFunc, + Optional('gkbar'): numberOrStringFunc, + Optional('gl'): numberOrStringFunc, + Optional('el'): numberOrStringFunc, }, Optional('pas'): { # another one - Optional('g'): Or(int, float, str), - Optional('e'): Or(int, float, str), + Optional('g'): numberOrStringFunc, + Optional('e'): numberOrStringFunc, }, Optional(str): { # other possibilities (nonlinear mechanisms: .mod) Optional( @@ -256,7 +259,7 @@ def cell_specs(context): # Optional('synMechs'): [{'label': str, 'loc': Or(int,float)}] Optional('pointps'): { str: { - 'mod': And( str, lambda s: __isPointpModel(s, context)), + 'mod': And( str, And(lambda s: __isPointpModel(s, context), error='no pointp'), error='Mod is bad'), # 'mod': And( str, And(lambda s: __isPointpModel(s, context), error='Bebe'), error='Meme'), Optional('loc'): Or(int, float), Optional('vref'): str, # voltage calculated in the .mod Optional('synList'): [ @@ -353,7 +356,7 @@ def cell_specs(context): # Other options are possible, for example those from IntFire1, etcetera. Optional(str): object, }, - Optional('vars'): {Optional(str): Or(int, float, str)}, + Optional('vars'): {Optional(str): numberOrStringFunc}, Optional(str): object, } } @@ -375,11 +378,11 @@ def synmech_specs(context): # lambda s: return True, # Options for ExpSyn - Optional('tau'): Or(int, float, str), - Optional('e'): Or(int, float, str), + Optional('tau'): numberOrStringFunc, + Optional('e'): numberOrStringFunc, # Options for Exp2Syn - Optional('tau1'): Or(int, float, str), - Optional('tau2'): Or(int, float, str), + Optional('tau1'): numberOrStringFunc, + Optional('tau2'): numberOrStringFunc, # Optional('e'): Or(int,float), # already set in ExpSyn Optional('pointerParams'): { 'target_var': str, @@ -442,9 +445,9 @@ def conn_specs(context): Optional(str): Or(Or(str, int, float), [Or(str, int, float)]), }, Optional('connFunc'): lambda s: s in ['fullConn', 'probConn', 'convConn', 'divConn', 'fromListConn'], - Optional('probability'): Or(int, float, str), # it can also be a string-based function - Optional('convergence'): Or(int, float, str), # it can also be a string-based function - Optional('divergence'): Or(int, float, str), # it can also be a string-based function + Optional('probability'): numberOrStringFunc, # it can also be a string-based function + Optional('convergence'): numberOrStringFunc, # it can also be a string-based function + Optional('divergence'): numberOrStringFunc, # it can also be a string-based function Optional('connList'): Or( [And (Or(tuple, list), lambda s: len(s) == 2 and all(isinstance(n, int) for n in s))], # list of 2-element lists/tuples of two ints (pre, post) lambda s: isinstance(s, np.ndarray) and s.shape[1] == 2 and s.dtype == 'int' # np.array of shape (x, 2) of ints @@ -795,7 +798,7 @@ def rxd_specs(): str: { 'reactant': str, # validity of the expression will not be checked 'product': str, # validity of the expression will not be checked - 'rate_f': Or(int, float, str), + 'rate_f': numberOrStringFunc, Optional('rate_b'): Or(int, float, str, None), Optional('regions'): Or(str, [str], [None]), Optional('custom_dynamics'): Or(bool, None) @@ -815,7 +818,7 @@ def rxd_specs(): str: { 'reactant': str, # validity of the expression will not be checked 'product': str, # validity of the expression will not be checked - 'rate_f': Or(int, float, str), + 'rate_f': numberOrStringFunc, Optional('rate_b'): Or(int, float, str, None), Optional('regions'): Or(str, [str], [None]), Optional('custom_dynamics'): Or(bool, None), @@ -827,7 +830,7 @@ def rxd_specs(): Optional('rates'): { str: { 'species': Or(str, [str]), # string-based specification (see rxd_net example) - 'rate': Or(int, float, str), + 'rate': numberOrStringFunc, Optional('regions'): Or(str, [str], [None]), Optional('membrane_flux'): bool, } @@ -844,19 +847,20 @@ def validateNetParams(net_params, printWarnings=True): global __mechVarList __mechVarList = None - def validate(data, specs, label): + def validate(data, specs, component): schema = Schema(specs) try: valid = schema.validate(data) - print(f" Successfully validated {label}") - validatedSchemas[label] = valid - except SchemaError as error: - msg = __parseErrorMessage(error.autos, origIndent=' ') + print(f" Successfully validated {component}") + validatedSchemas[component] = valid + except SchemaError as origError: + error = ValidationError(component, origError) + failedSchemas.append(error) + if printWarnings: - print(f"\n Error validating {label}:") - print(msg + "\n") - failedSchemas.append((label, error, msg)) + print(f"\n Error validating {component}:") + print(error.formattedMessage(baseIndent=' ') + "\n") context = ValidationContext(net_params) @@ -924,17 +928,12 @@ def validate(data, specs, label): ) if net_params.rxdParams: - validate( net_params.rxdParams, rxd_specs(), 'rxdParams' ) - if len(validatedSchemas) == 0: - validatedSchemas = None - if len(failedSchemas) == 0: - failedSchemas = None return validatedSchemas, failedSchemas # utils @@ -989,27 +988,54 @@ def isModel(name, modelType): return all(isModel(n, modelType) for n in name) return isModel(name, modelType) -def __parseErrorMessage(msg, origIndent=''): - import re - pattern = re.compile("key ('.*') error:", re.IGNORECASE) # TODO: need to freeze `schema` version to make sure this pattern is preserved - keySeq = [] - other = [] - # convert dict keys sequence to single string - for line in msg: - if line is None: line = '' - matches = pattern.match(line) - if matches: - matches = matches.groups() - if len(matches) > 0: - keySeq.append(matches[0]) # presume only one match - elif line != '': - other.append(line) - if len(keySeq) > 0: - newln = '\n' + origIndent + ' ' - other = newln + newln.join(other) - return f"{origIndent}Error in {' -> '.join(keySeq)}:{other}" - else: - return msg + +class ValidationError(object): + + def __init__(self, component, error): + self.component = component + self.originalError = error + + self.keyPath, self.summary = self.__parseErrorMessage() + + def formattedMessage(self, baseIndent=''): + + message = "" + if len(self.keyPath) > 0: + keySeq = ' -> '.join(self.keyPath) + message += f"{baseIndent}Error in {keySeq}:" + + newLine = '\n' + baseIndent + ' ' + if len(self.summary): + message += newLine + newLine.join(self.summary) + else: + message += newLine + '' + + return message + + def __parseErrorMessage(self, indent=''): + import re + pattern = re.compile("key ('.*') error:", re.IGNORECASE) # TODO: need to freeze `schema` version to make sure this pattern is preserved + keySeq = [] + other = [] + err = self.originalError + # convert dict keys sequence to single string + for line in err.autos: + if line is None: line = '' + matches = pattern.match(line) + if matches: + matches = matches.groups() + if len(matches) > 0: + keySeq.append(matches[0]) # presume only one match + elif line != '': + other.append(line) + + errors = [e for e in err.errors if e is not None] + if errors: # custom errors (provided by netpyne validator) + summary = [errors[-1]] # take the last error (and put into list for consistency) + else: # auto-generated by `schema` + summary = other + + return keySeq, summary @@ -1026,24 +1052,32 @@ def checkValidation(): def checkModelValid(index): print(f'PROCESSSING {index}') _, netParams = sim.loadModel(index, loadMechs=True, ignoreMechAlreadyExistsError=True) - _, failed = validator.validateNetParams(net_params=netParams) + valid, failed = validator.validateNetParams(net_params=netParams) if failed: print(f'FOUND {len(failed)} ERRORS IN {index}') - for (compName, error, errorAutos) in failed: + for compName in [f.component for f in failed]: print(compName) else: print(f'VALIDATION SUCCEEDED FOR {index}') + return valid, failed os.chdir('examples') try: + valid, failed = [], [] + # TODO: for some reason, the models below fail to load when run in order of for-loop below. # Works okay when run individually though... exceptionFromLoop = ['batchCellMapping/index.npjson', 'batchCell/index.npjson'] for index in [index for index in glob.glob('*/*.npjson') if index not in exceptionFromLoop ]: - checkModelValid(index) + v, f = checkModelValid(index) + valid.extend(v) + failed.extend(f) except Exception as e: print(f"FAILED VALIDATING EXAMPLES: {e}") - os.chdir('..') \ No newline at end of file + print(f'================\nValidation summary: {len(failed)} failed\n') + + os.chdir('..') + return failed \ No newline at end of file diff --git a/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py b/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py index ae317bf45..dab55a4da 100644 --- a/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py +++ b/netpyne/tutorials/voltage_movie_tut/voltage_movie_tut.py @@ -112,8 +112,10 @@ } ## Then we can replace `sim.runSim()` with: +def intervalFunc(t, **kwargs): + sim.analysis.plotShape(**kwargs) -sim.runSimWithIntervalFunc(1.0, sim.analysis.plotShape, timeRange=[10, 20], funcArgs=plotArgs) +sim.runSimWithIntervalFunc(1.0, intervalFunc, timeRange=[10, 20], funcArgs=plotArgs) ## This will execute `sim.analysis.plotShape` every 1.0 ms from 10 to 20 ms in the simulation and feed it the plotArgs dictionary we created above.