Skip to content

Commit 25f058e

Browse files
author
Dilawar Singh
committed
If nml user has set id of the gate to be 'x', 'y' or 'z' then load the
dynamics into HHChannels's gateX, gateY, or gateZ dyamics.
1 parent 52785b8 commit 25f058e

File tree

1 file changed

+182
-109
lines changed

1 file changed

+182
-109
lines changed

python/moose/neuroml2/reader.py

Lines changed: 182 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,42 @@
4343
"********************************************************************")
4444

4545

46+
# these gates are available in moose. These are prefixed by 'gate'
47+
_validMooseHHGateIds = ['X', 'Y', 'Z']
48+
4649
def _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+
5357
def _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

5683
def _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

Comments
 (0)