4343 "********************************************************************" )
4444
4545
46+ # these gates are available in moose. These are prefixed by 'gate'
47+ _validMooseHHGateIds = ['X' , 'Y' , 'Z' ]
48+
4649def _unique (ls ):
47- res = [ ]
50+ res = []
4851 for l in ls :
4952 if l not in res :
5053 res .append (l )
5154 return res
5255
56+
5357def _whichGate (chan ):
54- return chan .name [- 1 ]
58+ global _validMooseHHGateIds
59+ c = chan .name [- 1 ]
60+ assert c in _validMooseHHGateIds
61+ return c
62+
63+ def _pairNmlGateWithMooseGates (mGates , nmlGates ):
64+ """Return moose gate id from nml.HHGate
65+ """
66+ global _validMooseHHGateIds
67+ # deep copy
68+ mooseGatesMap = {_whichGate (x ) : x for x in mGates }
69+ availableMooseGates = _validMooseHHGateIds [:]
70+ mapping = {}
71+ for nmlGate in nmlGates :
72+ if nmlGate is None :
73+ continue
74+ if hasattr (nmlGate , 'id' ) and nmlGate .id :
75+ mapping [nmlGate .id .upper ()] = nmlGate
76+ availableMooseGates .remove (nmlGate .id .upper ())
77+ else :
78+ mapping [availableMooseGates .pop (0 )] = nmlGate
79+
80+ # Now replace 'X', 'Y', 'Z' with moose gates.
81+ return [(mooseGatesMap [x ], mapping [x ]) for x in mapping ]
5582
5683def _isConcDep (ct ):
5784 """_isConcDep
@@ -75,9 +102,10 @@ def _findCaConc():
75102 Find a suitable CaConc for computing HHGate tables.
76103 This is a hack, though it is likely to work in most cases.
77104 """
78- caConcs = moose .wildcardFind ( '/library/##[TYPE=CaConc]' )
105+ caConcs = moose .wildcardFind ('/library/##[TYPE=CaConc]' )
79106 assert len (caConcs ) == 1 , "No moose.CaConc found. Currently moose \
80107 supports HHChannel which depends only on moose.CaConc ."
108+
81109 return caConcs [0 ]
82110
83111
@@ -200,7 +228,6 @@ def __init__(self, verbose=False):
200228 # Just in case.
201229 self ._variables = {}
202230
203-
204231 def read (self , filename , symmetric = True ):
205232 self .doc = loaders .read_neuroml2_file (filename ,
206233 include_includes = True ,
@@ -464,8 +491,9 @@ def isPassiveChan(self, chan):
464491 'HHExpLinearRate' : linoid2
465492 }
466493
467- def calculateRateFn (self , ratefn , vmin , vmax , tablen = 3000 , vShift = '0mV' ):
494+ def calculateRateFn (self , ratefn , mgate , vmin , vmax , tablen = 3000 , vShift = '0mV' ):
468495 """Returns A / B table from ngate."""
496+
469497 tab = np .linspace (vmin , vmax , tablen )
470498 if self ._is_standard_nml_rate (ratefn ):
471499 midpoint , rate , scale = map (
@@ -476,24 +504,62 @@ def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'):
476504 if ratefn .type != ct .name :
477505 continue
478506 logger_ .info ("Using %s to evaluate rate" % ct .name )
479- rate = []
480- for v in tab :
481- req_vars = {'v' :'%sV' % v ,'vShift' :vShift ,'temperature' :self ._getTemperature ()}
482- req_vars .update (self ._variables )
483- vals = pynml .evaluate_component (ct , req_variables = req_vars )
484- '''print(vals)'''
485- if 'x' in vals :
486- rate .append (vals ['x' ])
487- if 't' in vals :
488- rate .append (vals ['t' ])
489- if 'r' in vals :
490- rate .append (vals ['r' ])
491- return np .array (rate )
492-
493- def calculateRateFnCaDep (self , ratefn , ca , camin , camax , tablen = 3000 , vShift = '0mV' ):
507+ if not _isConcDep (ct ):
508+ return self ._computeRateFn (ct , tab )
509+ else :
510+ ca = _findCaConc ()
511+ assert _whichGate (mgate ) == 'Z' , mgate
512+ return self ._computeRateFnCa (ca , ct , tab , vShift = vShift )
513+
514+ def _computeRateFnCa (self , ca , ct , tab , vShift ):
515+ rate = []
516+ for v in tab :
517+ req_vars = {
518+ ca .name : '%sV' % v ,
519+ 'vShift' : vShift ,
520+ 'temperature' : self ._getTemperature ()
521+ }
522+ req_vars .update (self ._variables )
523+ vals = pynml .evaluate_component (ct , req_variables = req_vars )
524+ '''print(vals)'''
525+ if 'x' in vals :
526+ rate .append (vals ['x' ])
527+ if 't' in vals :
528+ rate .append (vals ['t' ])
529+ if 'r' in vals :
530+ rate .append (vals ['r' ])
531+ return np .array (rate )
532+
533+ def _computeRateFn (self , ct , tab , vShift ):
534+ rate = []
535+ for v in tab :
536+ req_vars = {
537+ 'v' : '%sV' % v ,
538+ 'vShift' : vShift ,
539+ 'temperature' : self ._getTemperature ()
540+ }
541+ req_vars .update (self ._variables )
542+ vals = pynml .evaluate_component (ct , req_variables = req_vars )
543+ '''print(vals)'''
544+ if 'x' in vals :
545+ rate .append (vals ['x' ])
546+ if 't' in vals :
547+ rate .append (vals ['t' ])
548+ if 'r' in vals :
549+ rate .append (vals ['r' ])
550+ return np .array (rate )
551+
552+ def calculateRateFnCaDep (self ,
553+ ratefn ,
554+ ca ,
555+ camin ,
556+ camax ,
557+ tablen = 3000 ,
558+ vShift = '0mV' ):
494559 """Returns A / B table from ngate.
495560
496- FIXME: Merge with calculateRateFn
561+ This function compute rate which depends on ca.
562+ This must go into Z gate.
497563 """
498564 tab = np .linspace (camin , camax , tablen )
499565 if self ._is_standard_nml_rate (ratefn ):
@@ -504,10 +570,15 @@ def calculateRateFnCaDep(self, ratefn, ca, camin, camax, tablen=3000, vShift='0m
504570 for ct in self .doc .ComponentType :
505571 if ratefn .type != ct .name :
506572 continue
507- logger_ .info ("Using %s to evaluate rate (caConc dependant)" % ct .name )
573+ logger_ .info ("Using %s to evaluate rate (caConc dependant)" %
574+ ct .name )
508575 rate = []
509576 for v in tab :
510- req_vars = {ca .name :'%sV' % v ,'vShift' :vShift ,'temperature' :self ._getTemperature ()}
577+ req_vars = {
578+ ca .name : '%sV' % v ,
579+ 'vShift' : vShift ,
580+ 'temperature' : self ._getTemperature ()
581+ }
511582 vals = pynml .evaluate_component (ct , req_variables = req_vars )
512583 '''print(vals)'''
513584 if 'x' in vals :
@@ -615,107 +686,109 @@ def _is_standard_nml_rate(self, rate):
615686 rate .type == 'HHSigmoidVariable'
616687
617688 def createHHChannel (self , chan , vmin = - 150e-3 , vmax = 100e-3 , vdivs = 5000 ):
618- mchan = moose .HHChannel ('%s/%s' % (self .lib .path , chan .id ))
619- mgates = map (moose .element , (mchan .gateX , mchan .gateY , mchan .gateZ ))
689+ path = '%s/%s' % (self .lib .path , chan .id )
690+ if moose .exists (path ):
691+ mchan = moose .element (path )
692+ else :
693+ mchan = moose .HHChannel (path )
694+ mgates = [
695+ moose .element (g ) for g in [mchan .gateX , mchan .gateY , mchan .gateZ ]
696+ ]
620697
621698 # We handle only up to 3 gates in HHCHannel
622- assert ( len (chan .gate_hh_rates ) <= 3 )
699+ assert len (chan .gate_hh_rates ) <= 3 , "No more than 3 gates"
623700
624701 if self .verbose :
625702 print ('== Creating channel: %s (%s) -> %s (%s)' %
626703 (chan .id , chan .gate_hh_rates , mchan , mgates ))
704+
627705 all_gates = chan .gates + chan .gate_hh_rates
628- for ngate , mgate in zip (all_gates , mgates ):
629- if ngate is None :
630- continue
631- logger_ .info ('whichGate(mgate) %s %s' % (mgate , _whichGate (mgate )))
632- if _whichGate (mgate ) == 'X' :
633- mchan .Xpower = ngate .instances
634- elif _whichGate (mgate ) == 'Y' :
635- mchan .Ypower = ngate .instances
636- elif _whichGate (mgate ) == 'Z' :
637- mchan .Zpower = ngate .instance
638- mgate .min = vmin
639- mgate .max = vmax
640- mgate .divs = vdivs
641-
642- # I saw only examples of GateHHRates in
643- # HH-channels, the meaning of forwardRate and
644- # reverseRate and steadyState are not clear in the
645- # classes GateHHRatesInf, GateHHRatesTau and in
646- # FateHHTauInf the meaning of timeCourse and
647- # steady state is not obvious. Is the last one
648- # refering to tau_inf and m_inf??
649- fwd = ngate .forward_rate
650- rev = ngate .reverse_rate
651-
652- self .paths_to_chan_elements [
653- '%s/%s' %
654- (chan .id , ngate .id )] = '%s/%s' % (chan .id , mgate .name )
655-
656- q10_scale = 1
657- if ngate .q10_settings :
658- if ngate .q10_settings .type == 'q10Fixed' :
659- q10_scale = float (ngate .q10_settings .fixed_q10 )
660- elif ngate .q10_settings .type == 'q10ExpTemp' :
661- q10_scale = math .pow (
662- float (ngate .q10_settings .q10_factor ),
663- (self ._getTemperature () -
664- SI (ngate .q10_settings .experimental_temp )) / 10 )
665- #print('Q10: %s; %s; %s; %s'%(ngate.q10_settings.q10_factor, self._getTemperature(),SI(ngate.q10_settings.experimental_temp),q10_scale))
666- else :
667- raise Exception (
668- 'Unknown Q10 scaling type %s: %s' %
669- (ngate .q10_settings .type , ngate .q10_settings ))
670706
671- logger_ .info (' === Gate: %s; %s; %s; %s; %s; scale=%s' %
672- (ngate .id , mgate .path , mchan .Xpower , fwd , rev , q10_scale ))
707+ # If user set bnml channels' id to 'x', 'y' or 'z' then pair this gate
708+ # with moose.HHChannel's gateX, gateY, gateZ respectively. Else pair
709+ # them with gateX, gateY, gateZ acording to list order.
710+ for mgate , ngate in _pairNmlGateWithMooseGates (mgates , all_gates ):
711+ self ._addGateToHHChannel (chan , mchan , mgate , ngate , vmin , vmax ,
712+ vdivs )
713+ logger_ .debug ('== Created' , mchan .path , 'for' , chan .id )
714+ return mchan
673715
674- print (fwd , rev )
675- quit ()
716+ def _addGateToHHChannel (self , chan , mchan , mgate , ngate , vmin , vmax ,
717+ vdivs ):
718+ """Add gateX, gateY, gateZ etc to moose.HHChannel (mchan).
676719
677- if (fwd is not None ) and (rev is not None ):
678- # Note: MOOSE HHGate are either voltage of concentration
679- # dependant. Here we figure out if nml description of gate is
680- # concentration dependant or not.
681- if not _isConcDep (fwd ):
682- alpha = self .calculateRateFn (fwd , vmin , vmax , vdivs )
683- else :
684- ca = _findCaConc ()
685- alpha = self .calculateRateFnCaDep (fwd , ca , vmin , vmax , vdivs )
720+ Each gate can be voltage dependant and/or concentration dependant.
721+ Only caConc dependant channels are supported.
722+ """
686723
687- if not _isConcDep (rev ):
688- beta = self .calculateRateFnCaDep (rev , vmin , vmax , vdivs )
689- else :
690- ca = _findCaConc ()
691- beta = self .calculateRateFn (rev , ca , vmin , vmax , vdivs )
692-
693- mgate .tableA = q10_scale * (alpha )
694- mgate .tableB = q10_scale * (alpha + beta )
695-
696- # Assuming the meaning of the elements in GateHHTauInf ...
697- if hasattr (ngate ,'time_course' ) and hasattr (ngate ,'steady_state' ) \
698- and (ngate .time_course is not None ) and (ngate .steady_state is not None ):
699- tau = ngate .time_course
700- inf = ngate .steady_state
701- tau = self .calculateRateFn (tau , vmin , vmax , vdivs )
724+ # set mgate.Xpower, .Ypower etc.
725+ setattr (mchan , _whichGate (mgate ) + 'power' , ngate .instances )
726+
727+ mgate .min = vmin
728+ mgate .max = vmax
729+ mgate .divs = vdivs
730+
731+ # Note by Padraig:
732+ # ---------------
733+ # I saw only examples of GateHHRates in HH-channels, the meaning of
734+ # forwardRate and reverseRate and steadyState are not clear in the
735+ # classes GateHHRatesInf, GateHHRatesTau and in FateHHTauInf the
736+ # meaning of timeCourse and steady state is not obvious. Is the last
737+ # one # refering to tau_inf and m_inf??
738+ fwd = ngate .forward_rate
739+ rev = ngate .reverse_rate
740+
741+ self .paths_to_chan_elements ['%s/%s' % (chan .id , ngate .id )] = \
742+ '%s/%s' % (chan .id , mgate .name )
743+
744+ q10_scale = 1
745+ if ngate .q10_settings :
746+ if ngate .q10_settings .type == 'q10Fixed' :
747+ q10_scale = float (ngate .q10_settings .fixed_q10 )
748+ elif ngate .q10_settings .type == 'q10ExpTemp' :
749+ q10_scale = math .pow (
750+ float (ngate .q10_settings .q10_factor ),
751+ (self ._getTemperature () -
752+ SI (ngate .q10_settings .experimental_temp )) / 10 )
753+ logger_ .debug (
754+ 'Q10: %s; %s; %s; %s' %
755+ (ngate .q10_settings .q10_factor , self ._getTemperature (),
756+ SI (ngate .q10_settings .experimental_temp ), q10_scale ))
757+ else :
758+ raise Exception ('Unknown Q10 scaling type %s: %s' %
759+ (ngate .q10_settings .type , ngate .q10_settings ))
760+ logger_ .info (' === Gate: %s; %s; %s; %s; %s; scale=%s' %
761+ (ngate .id , mgate .path , mchan .Xpower , fwd , rev , q10_scale ))
762+
763+ if (fwd is not None ) and (rev is not None ):
764+ # Note: MOOSE HHGate are either voltage of concentration
765+ # dependant. Here we figure out if nml description of gate is
766+ # concentration dependant or not.
767+ alpha = self .calculateRateFn (fwd , mgate , vmin , vmax , vdivs )
768+ beta = self .calculateRateFn (rev , mgate , vmin , vmax , vdivs )
769+
770+ mgate .tableA = q10_scale * (alpha )
771+ mgate .tableB = q10_scale * (alpha + beta )
772+
773+ # Assuming the meaning of the elements in GateHHTauInf ...
774+ if hasattr (ngate ,'time_course' ) and hasattr (ngate ,'steady_state' ) \
775+ and (ngate .time_course is not None ) and (ngate .steady_state is not None ):
776+ tau = ngate .time_course
777+ inf = ngate .steady_state
778+ tau = self .calculateRateFn (tau , mgate , vmin , vmax , vdivs )
779+ inf = self .calculateRateFn (inf , mgate , vmin , vmax , vdivs )
780+ mgate .tableA = q10_scale * (inf / tau )
781+ mgate .tableB = q10_scale * (1 / tau )
782+
783+ if hasattr (ngate , 'steady_state' ) and (ngate .time_course is None ) \
784+ and (ngate .steady_state is not None ):
785+ inf = ngate .steady_state
786+ tau = 1 / (alpha + beta )
787+ if (inf is not None ):
702788 inf = self .calculateRateFn (inf , vmin , vmax , vdivs )
703789 mgate .tableA = q10_scale * (inf / tau )
704790 mgate .tableB = q10_scale * (1 / tau )
705791
706- if hasattr (ngate , 'steady_state' ) and (ngate .time_course is None ) \
707- and (ngate .steady_state is not None ):
708- inf = ngate .steady_state
709- tau = 1 / (alpha + beta )
710- if (inf is not None ):
711- inf = self .calculateRateFn (inf , vmin , vmax , vdivs )
712- mgate .tableA = q10_scale * (inf / tau )
713- mgate .tableB = q10_scale * (1 / tau )
714-
715- if self .verbose :
716- print (self .filename , '== Created' , mchan .path , 'for' , chan .id )
717- return mchan
718-
719792 def createPassiveChannel (self , chan ):
720793 mchan = moose .Leakage ('%s/%s' % (self .lib .path , chan .id ))
721794 if self .verbose :
0 commit comments