def saveRecords(records, xvec=None, **kwargs): """saveRecords Given a dictionary of data with (key, numpy array) pair, it saves them to a file 'outfile' :param outfile :param dataDict: :param **kwargs: comment: Adds comments below the header. """ if len(records) == 0: pu.warn("No data in dictionary to save.") return False outfile = kwargs.get('outfile', 'data.moose') clock = moose.Clock('/clock') assert clock.currentTime > 0 yvecs = [] text = "time," + ",".join([str(x) for x in records]) for k in records: try: yvec = records[k].vector except AttributeError as e: yevc = records[k] yvecs.append(yvec) xvec = np.linspace(0, clock.currentTime, len(yvecs[0])) yvecs = [xvec] + yvecs if kwargs.get('comment', ''): text += ("\n" + kwargs['comment']) np.savetxt(outfile, np.array(yvecs).T, delimiter=',', header=text) pu.info("Done writing data to %s" % outfile)
def saveRecords(records, xvec = None, **kwargs): """saveRecords Given a dictionary of data with (key, numpy array) pair, it saves them to a file 'outfile' :param outfile :param dataDict: :param **kwargs: comment: Adds comments below the header. """ if len(records) == 0: pu.warn("No data in dictionary to save.") return False outfile = kwargs.get('outfile', 'data.moose') clock = moose.Clock('/clock') assert clock.currentTime > 0 yvecs = [ ] text = "time," + ",".join([ str(x) for x in records ]) for k in records: try: yvec = records[k].vector except AtrributeError as e: yevc = records[k] yvecs.append(yvec) xvec = np.linspace(0, clock.currentTime, len(yvecs[0])) yvecs = [ xvec ] + yvecs if kwargs.get('comment', ''): text += ("\n" + kwargs['comment'] ) np.savetxt(outfile, np.array(yvecs).T, delimiter=',' , header = text) pu.info("Done writing data to %s" % outfile)
def readMorphMLFromFile(self,filename,params={}): """ specify params for this MorphML file as a dict: presently combineSegments and createPotentialSynapses are implemented. See readMorphML(). See also nml_params in __init__(). returns { cellname1 : (segDict,cableDict), ... } see readMorphML(...) for segDict and cableDict """ pu.info("Reading morphology from %s" % filename) try: tree = ET.parse(filename) except Exception as e: pu.fatal("Failed to load morphology from file %s" % filename) neuroml_element = tree.getroot() cellsDict = {} for cell in neuroml_element.findall('.//{'+self.neuroml+'}cell'): if 'lengthUnits' in list(neuroml_element.attrib.keys()): lengthUnits = neuroml_element.attrib['lengthUnits'] else: lengthUnits = 'micrometer' cellDict = self.readMorphML(cell,params,lengthUnits) cellsDict.update(cellDict) return cellsDict
def readSynapseML(self, synapseElement, units="SI units"): if 'Physiological Units' in units: # see pg 219 (sec 13.2) of Book of Genesis Vfactor = 1e-3 # V from mV Tfactor = 1e-3 # s from ms Gfactor = 1e-3 # S from mS elif 'SI Units' in units: Vfactor = 1.0 Tfactor = 1.0 Gfactor = 1.0 else: pu.fatal("Wrong units %s exiting ..." % units) sys.exit(1) if not moose.exists('/library'): moose.Neutral('/library') synname = synapseElement.attrib['name'] if utils.neuroml_debug: pu.info("Loading synapse : %s into /library" % synname) moosesynapse = moose.SynChan('/library/' + synname) doub_exp_syn = synapseElement.find('./{' + self.cml + '}doub_exp_syn') moosesynapse.Ek = float( doub_exp_syn.attrib['reversal_potential']) * Vfactor moosesynapse.Gbar = float( doub_exp_syn.attrib['max_conductance']) * Gfactor moosesynapse.tau1 = float( doub_exp_syn.attrib['rise_time']) * Tfactor # seconds moosesynapse.tau2 = float( doub_exp_syn.attrib['decay_time']) * Tfactor # seconds ## The delay and weight can be set only after connecting a spike event generator. ## delay and weight are arrays: multiple event messages can be connected to a single synapse ## graded synapses are not supported by neuroml, so set to False here, ## see my Demo/neuroml/lobster_pyloric/STG_net.py for how to still have graded synapses moosesynapse_graded = moose.Mstring(moosesynapse.path + '/graded') moosesynapse_graded.value = 'False' moosesynapse_mgblock = moose.Mstring(moosesynapse.path + '/mgblockStr') moosesynapse_mgblock.value = 'False' ## check if STDP synapse is present or not stdp_syn = synapseElement.find('./{' + self.cml + '}stdp_syn') if stdp_syn is None: moosesynhandler = moose.SimpleSynHandler('/library/' + synname + '/handler') else: moosesynhandler = moose.STDPSynHandler('/library/' + synname + '/handler') moosesynhandler.aPlus0 = float(stdp_syn.attrib['del_weight_ltp']) moosesynhandler.aMinus0 = float(stdp_syn.attrib['del_weight_ltd']) moosesynhandler.tauPlus = float(stdp_syn.attrib['tau_ltp']) moosesynhandler.tauMinus = float(stdp_syn.attrib['tau_ltd']) moosesynhandler.weightMax = float( stdp_syn.attrib['max_syn_weight']) moosesynhandler.weightMin = 0.0 ## connect the SimpleSynHandler or the STDPSynHandler to the SynChan (double exp) moose.connect(moosesynhandler, 'activationOut', moosesynapse, 'activation')
def plotRecords(records, xvec=None, **kwargs): """Wrapper """ dataDict = OrderedDict() try: for k in sorted(records.keys(), key=str.lower): dataDict[k] = records[k] except Exception as e: dataDict = records legend = kwargs.get('legend', True) outfile = kwargs.get('outfile', None) subplot = kwargs.get('subplot', False) filters = [x.lower() for x in kwargs.get('filter', [])] plt.figure(figsize=(10, 1.5 * len(dataDict))) #plt.rcParams.update( { 'font-size' : 10 } ) for i, k in enumerate(dataDict): pu.info("+ Plotting for %s" % k) plotThis = False if not filters: plotThis = True for accept in filters: if accept in k.lower(): plotThis = True break if plotThis: if not subplot: yvec = dataDict[k].vector plotVector(yvec, xvec, label=k, **kwargs) else: plt.subplot(len(dataDict), 1, i + 1) yvec = dataDict[k].vector plotVector(yvec, xvec, label=k, **kwargs) # title in Image. if 'title' in kwargs: plt.title(kwargs['title']) if subplot: try: plt.tight_layout() except: pass if outfile: pu.info("Writing plot to %s" % outfile) plt.savefig("%s" % outfile, transparent=True) else: plt.show() plt.close()
def plotRecords(records, xvec = None, **kwargs): """Wrapper """ dataDict = OrderedDict( ) try: for k in sorted(records.keys(), key=str.lower): dataDict[k] = records[k] except Exception as e: dataDict = records legend = kwargs.get('legend', True) outfile = kwargs.get('outfile', None) subplot = kwargs.get('subplot', False) filters = [ x.lower() for x in kwargs.get('filter', [])] plt.figure(figsize=(10, 1.5*len(dataDict))) #plt.rcParams.update( { 'font-size' : 10 } ) for i, k in enumerate(dataDict): pu.info("+ Plotting for %s" % k) plotThis = False if not filters: plotThis = True for accept in filters: if accept in k.lower(): plotThis = True break if plotThis: if not subplot: yvec = dataDict[k].vector plotVector(yvec, xvec, label=k, **kwargs) else: plt.subplot(len(dataDict), 1, i+1) yvec = dataDict[k].vector plotVector(yvec, xvec, label=k, **kwargs) # title in Image. if 'title' in kwargs: plt.title(kwargs['title']) if subplot: try: plt.tight_layout() except: pass if outfile: pu.info("Writing plot to %s" % outfile) plt.savefig("%s" % outfile, transparent=True) else: plt.show() plt.close( )
def readIonConcML(self, ionConcElement, units="SI units"): if units == 'Physiological Units': # see pg 219 (sec 13.2) of Book of Genesis Vfactor = 1e-3 # V from mV Tfactor = 1e-3 # s from ms Gfactor = 1e1 # S/m^2 from mS/cm^2 concfactor = 1e6 # mol/m^3 from mol/cm^3 Lfactor = 1e-2 # m from cm Ifactor = 1e-6 # A from microA else: Vfactor = 1.0 Tfactor = 1.0 Gfactor = 1.0 concfactor = 1.0 Lfactor = 1.0 Ifactor = 1.0 moose.Neutral( '/library') # creates /library in MOOSE tree; elif present, wraps ionSpecies = ionConcElement.find('./{' + self.cml + '}ion_species') if ionSpecies is not None: if not 'ca' in ionSpecies.attrib['name']: pu.fatal( "Sorry, I cannot handle non-Ca-ion pools. Exiting ...") sys.exit(1) capoolName = ionConcElement.attrib['name'] pu.info("Loading Ca pool %s into /library ." % capoolName) caPool = moose.CaConc('/library/' + capoolName) poolModel = ionConcElement.find('./{' + self.cml + '}decaying_pool_model') caPool.CaBasal = float(poolModel.attrib['resting_conc']) * concfactor caPool.Ca_base = float(poolModel.attrib['resting_conc']) * concfactor if 'decay_constant' in poolModel.attrib: caPool.tau = float(poolModel.attrib['decay_constant']) * Tfactor elif 'inv_decay_constant' in poolModel.attrib: caPool.tau = 1.0 / float( poolModel.attrib['inv_decay_constant']) * Tfactor ## Only one of pool_volume_info or fixed_pool_info should be present, but not checking volInfo = poolModel.find('./{' + self.cml + '}pool_volume_info') if volInfo is not None: caPool.thick = float(volInfo.attrib['shell_thickness']) * Lfactor fixedPoolInfo = poolModel.find('./{' + self.cml + '}fixed_pool_info') if fixedPoolInfo is not None: ## Put in phi under the caPool, so that it can ## be used instead of thickness to set B (see section 19.2 in Book of Genesis) caPool_phi = moose.Mstring(caPool.path + '/phi') caPool_phi.value = str( float(fixedPoolInfo.attrib['phi']) * concfactor / Ifactor / Tfactor)
def readSynapseML(self, synapseElement, units="SI units"): if "Physiological Units" in units: # see pg 219 (sec 13.2) of Book of Genesis Vfactor = 1e-3 # V from mV Tfactor = 1e-3 # s from ms Gfactor = 1e-3 # S from mS elif "SI Units" in units: Vfactor = 1.0 Tfactor = 1.0 Gfactor = 1.0 else: pu.fatal("Wrong units %s exiting ..." % units) sys.exit(1) moose.Neutral("/library") # creates /library in MOOSE tree; elif present, wraps synname = synapseElement.attrib["name"] if utils.neuroml_debug: pu.info("Loading synapse : %s into /library" % synname) moosesynapse = moose.SynChan("/library/" + synname) doub_exp_syn = synapseElement.find("./{" + self.cml + "}doub_exp_syn") moosesynapse.Ek = float(doub_exp_syn.attrib["reversal_potential"]) * Vfactor moosesynapse.Gbar = float(doub_exp_syn.attrib["max_conductance"]) * Gfactor moosesynapse.tau1 = float(doub_exp_syn.attrib["rise_time"]) * Tfactor # seconds moosesynapse.tau2 = float(doub_exp_syn.attrib["decay_time"]) * Tfactor # seconds ## The delay and weight can be set only after connecting a spike event generator. ## delay and weight are arrays: multiple event messages can be connected to a single synapse ## graded synapses are not supported by neuroml, so set to False here, ## see my Demo/neuroml/lobster_pyloric/STG_net.py for how to still have graded synapses moosesynapse_graded = moose.Mstring(moosesynapse.path + "/graded") moosesynapse_graded.value = "False" moosesynapse_mgblock = moose.Mstring(moosesynapse.path + "/mgblockStr") moosesynapse_mgblock.value = "False" ## check if STDP synapse is present or not stdp_syn = synapseElement.find("./{" + self.cml + "}stdp_syn") if stdp_syn is None: moosesynhandler = moose.SimpleSynHandler("/library/" + synname + "/handler") else: moosesynhandler = moose.STDPSynHandler("/library/" + synname + "/handler") moosesynhandler.aPlus0 = float(stdp_syn.attrib["del_weight_ltp"]) moosesynhandler.aMinus0 = float(stdp_syn.attrib["del_weight_ltd"]) moosesynhandler.tauPlus = float(stdp_syn.attrib["tau_ltp"]) moosesynhandler.tauMinus = float(stdp_syn.attrib["tau_ltd"]) moosesynhandler.weightMax = float(stdp_syn.attrib["max_syn_weight"]) moosesynhandler.weightMin = 0.0 ## connect the SimpleSynHandler or the STDPSynHandler to the SynChan (double exp) moose.connect(moosesynhandler, "activationOut", moosesynapse, "activation")
def readIonConcML(self, ionConcElement, units="SI units"): if units == "Physiological Units": # see pg 219 (sec 13.2) of Book of Genesis Vfactor = 1e-3 # V from mV Tfactor = 1e-3 # s from ms Gfactor = 1e1 # S/m^2 from mS/cm^2 concfactor = 1e6 # mol/m^3 from mol/cm^3 Lfactor = 1e-2 # m from cm Ifactor = 1e-6 # A from microA else: Vfactor = 1.0 Tfactor = 1.0 Gfactor = 1.0 concfactor = 1.0 Lfactor = 1.0 Ifactor = 1.0 moose.Neutral("/library") # creates /library in MOOSE tree; elif present, wraps ionSpecies = ionConcElement.find("./{" + self.cml + "}ion_species") if ionSpecies is not None: if not "ca" in ionSpecies.attrib["name"]: pu.fatal("Sorry, I cannot handle non-Ca-ion pools. Exiting ...") sys.exit(1) capoolName = ionConcElement.attrib["name"] pu.info("Loading Ca pool %s into /library ." % capoolName) caPool = moose.CaConc("/library/" + capoolName) poolModel = ionConcElement.find("./{" + self.cml + "}decaying_pool_model") caPool.CaBasal = float(poolModel.attrib["resting_conc"]) * concfactor caPool.Ca_base = float(poolModel.attrib["resting_conc"]) * concfactor if "decay_constant" in poolModel.attrib: caPool.tau = float(poolModel.attrib["decay_constant"]) * Tfactor elif "inv_decay_constant" in poolModel.attrib: caPool.tau = 1.0 / float(poolModel.attrib["inv_decay_constant"]) * Tfactor ## Only one of pool_volume_info or fixed_pool_info should be present, but not checking volInfo = poolModel.find("./{" + self.cml + "}pool_volume_info") if volInfo is not None: caPool.thick = float(volInfo.attrib["shell_thickness"]) * Lfactor fixedPoolInfo = poolModel.find("./{" + self.cml + "}fixed_pool_info") if fixedPoolInfo is not None: ## Put in phi under the caPool, so that it can ## be used instead of thickness to set B (see section 19.2 in Book of Genesis) caPool_phi = moose.Mstring(caPool.path + "/phi") caPool_phi.value = str(float(fixedPoolInfo.attrib["phi"]) * concfactor / Ifactor / Tfactor)
def readChannelML(self, channelElement, params={}, units="SI units"): ## I first calculate all functions assuming a consistent system of units. ## While filling in the A and B tables, I just convert to SI. ## Also convert gmax and Erev. if 'Physiological Units' in units: # see pg 219 (sec 13.2) of Book of Genesis Vfactor = 1e-3 # V from mV Tfactor = 1e-3 # s from ms Gfactor = 1e1 # S/m^2 from mS/cm^2 concfactor = 1e6 # Mol = mol/m^-3 from mol/cm^-3 elif 'SI Units' in units: Vfactor = 1.0 Tfactor = 1.0 Gfactor = 1.0 concfactor = 1.0 else: raise RuntimeError("Wrong units %s. Existing" % units) if not moose.exists('/library'): moose.Neutral('/library') channel_name = channelElement.attrib['name'] if utils.neuroml_debug: pu.info("Loading channel %s into /library" % channel_name) IVrelation = channelElement.find('./{' + self.cml + '}current_voltage_relation') intfire = IVrelation.find('./{' + self.cml + '}integrate_and_fire') if intfire is not None: ## Below params need to be set while making an LIF compartment moosechannel = moose.Neutral('/library/' + channel_name) moosechannelval = moose.Mstring(moosechannel.path + '/vReset') moosechannelval.value = str( float(intfire.attrib['v_reset']) * Vfactor) moosechannelval = moose.Mstring(moosechannel.path + '/thresh') moosechannelval.value = str( float(intfire.attrib['threshold']) * Vfactor) moosechannelval = moose.Mstring(moosechannel.path + '/refracT') moosechannelval.value = str( float(intfire.attrib['t_refrac']) * Tfactor) ## refracG is currently not supported by moose.LIF ## Confirm if g_refrac is a conductance density or not? ## assuming g_refrac is a conductance density below moosechannelval = moose.Mstring(moosechannel.path + '/refracG') moosechannelval.value = str( float(intfire.attrib['g_refrac']) * Gfactor) ## create an Mstring saying this is an integrate_and_fire mechanism moosechannelval = moose.Mstring(moosechannel.path + '/integrate_and_fire') moosechannelval.value = 'True' return concdep = IVrelation.find('./{' + self.cml + '}conc_dependence') if concdep is None: moosechannel = moose.HHChannel('/library/' + channel_name) else: moosechannel = moose.HHChannel2D('/library/' + channel_name) if IVrelation.attrib['cond_law'] == "ohmic": moosechannel.Gbar = float( IVrelation.attrib['default_gmax']) * Gfactor moosechannel.Ek = float( IVrelation.attrib['default_erev']) * Vfactor moosechannelIon = moose.Mstring(moosechannel.path + '/ion') moosechannelIon.value = IVrelation.attrib['ion'] if concdep is not None: moosechannelIonDependency = moose.Mstring(moosechannel.path + '/ionDependency') moosechannelIonDependency.value = concdep.attrib['ion'] nernstnote = IVrelation.find('./{' + utils.meta_ns + '}notes') if nernstnote is not None: ## the text in nernstnote is "Nernst,Cout=<float>,z=<int>" nernst_params = nernstnote.text.split(',') if nernst_params[0] == 'Nernst': nernstMstring = moose.Mstring(moosechannel.path + '/nernst_str') nernstMstring.value = str( float(nernst_params[1].split('=')[1]) * concfactor ) + \ ',' + str( int(nernst_params[2].split('=')[1]) ) gates = IVrelation.findall('./{' + self.cml + '}gate') if len(gates) > 3: pu.fatal( "Sorry! Maximum x, y, and z (three) gates are possible in MOOSE/Genesis" ) sys.exit() gate_full_name = [ 'gateX', 'gateY', 'gateZ' ] # These are the names that MOOSE uses to create gates. ## if impl_prefs tag is present change VMIN, VMAX and NDIVS impl_prefs = channelElement.find('./{' + self.cml + '}impl_prefs') if impl_prefs is not None: table_settings = impl_prefs.find('./{' + self.cml + '}table_settings') ## some problem here... disable VMIN_here = float(table_settings.attrib['min_v']) VMAX_here = float(table_settings.attrib['max_v']) NDIVS_here = int(table_settings.attrib['table_divisions']) dv_here = (VMAX_here - VMIN_here) / NDIVS_here else: ## default VMIN, VMAX and dv are in SI ## convert them to current calculation units used by channel definition ## while loading into tables, convert them back to SI VMIN_here = utils.VMIN / Vfactor VMAX_here = utils.VMAX / Vfactor NDIVS_here = utils.NDIVS dv_here = utils.dv / Vfactor offset = IVrelation.find('./{' + self.cml + '}offset') if offset is None: vNegOffset = 0.0 else: vNegOffset = float(offset.attrib['value']) self.parameters = [] for parameter in channelElement.findall('.//{' + self.cml + '}parameter'): self.parameters.append( (parameter.attrib['name'], float(parameter.attrib['value']))) for num, gate in enumerate(gates): # if no q10settings tag, the q10factor remains 1.0 # if present but no gate attribute, then set q10factor # if there is a gate attribute, then set it only if gate attrib matches gate name self.q10factor = 1.0 self.gate_name = gate.attrib['name'] for q10settings in IVrelation.findall('./{' + self.cml + '}q10_settings'): ## self.temperature from neuro.utils if 'gate' in q10settings.attrib: if q10settings.attrib['gate'] == self.gate_name: self.setQ10(q10settings) break else: self.setQ10(q10settings) ############### HHChannel2D crashing on setting Xpower! #### temperamental! If you print something before, it gives cannot creategate from copied channel, else crashes ## Setting power first. This is necessary because it also ## initializes the gate's internal data structures as a side ## effect. Alternatively, gates can be initialized explicitly ## by calling HHChannel.createGate(). gate_power = float(gate.get('instances')) if num == 0: moosechannel.Xpower = gate_power if concdep is not None: moosechannel.Xindex = "VOLT_C1_INDEX" elif num == 1: moosechannel.Ypower = gate_power if concdep is not None: moosechannel.Yindex = "VOLT_C1_INDEX" elif num == 2: moosechannel.Zpower = gate_power if concdep is not None: moosechannel.Zindex = "VOLT_C1_INDEX" ## Getting handle to gate using the gate's path. gate_path = moosechannel.path + '/' + gate_full_name[num] if concdep is None: if not moose.exists(gate_path): moosegate = moose.HHGate(gate_path) else: moosegate = moose.element(gate_path) ## set SI values inside MOOSE moosegate.min = VMIN_here * Vfactor moosegate.max = VMAX_here * Vfactor moosegate.divs = NDIVS_here ## V.IMP to get smooth curves, else even with 3000 divisions ## there are sudden transitions. moosegate.useInterpolation = True else: moosegate = moose.HHGate2D(gate_path) ##### If alpha and beta functions exist, make them here for transition in gate.findall('./{' + self.cml + '}transition'): ## make python functions with names of transitions... fn_name = transition.attrib['name'] ## I assume that transitions if present are called alpha and beta ## for forward and backward transitions... if fn_name in ['alpha', 'beta']: self.make_cml_function(transition, fn_name, concdep) else: pu.fatal("Unsupported transition %s" % fn_name) sys.exit() time_course = gate.find('./{' + self.cml + '}time_course') ## tau is divided by self.q10factor in make_function() ## thus, it gets divided irrespective of <time_course> tag present or not. if time_course is not None: self.make_cml_function(time_course, 'tau', concdep) steady_state = gate.find('./{' + self.cml + '}steady_state') if steady_state is not None: self.make_cml_function(steady_state, 'inf', concdep) if concdep is None: ca_name = '' # no Ca dependence else: ca_name = ',' + concdep.attrib[ 'variable_name'] # Ca dependence ## Create tau() and inf() if not present, from alpha() and beta() for fn_element,fn_name,fn_expr in [(time_course,'tau',"1/(alpha+beta)"),\ (steady_state,'inf',"alpha/(alpha+beta)")]: ## put in args for alpha and beta, could be v and Ca dep. expr_string = fn_expr.replace('alpha', 'self.alpha(v' + ca_name + ')') expr_string = expr_string.replace( 'beta', 'self.beta(v' + ca_name + ')') ## if time_course/steady_state are not present, ## then alpha annd beta transition elements should be present, and fns created. if fn_element is None: self.make_function(fn_name, 'generic', expr_string=expr_string, concdep=concdep) ## non Ca dependent channel if concdep is None: ## while calculating, use the units used in xml defn, ## while filling in table, I convert to SI units. v0 = VMIN_here - vNegOffset n_entries = NDIVS_here + 1 tableA = [0.0] * n_entries tableB = [0.0] * n_entries for i in range(n_entries): v = v0 + i * dv_here inf = self.inf(v) tau = self.tau(v) ## convert to SI before writing to table ## qfactor is already in inf and tau tableA[i] = inf / tau / Tfactor tableB[i] = 1.0 / tau / Tfactor moosegate.tableA = tableA moosegate.tableB = tableB ## Ca dependent channel else: ## UNITS: while calculating, use the units used in xml defn, ## while filling in table, I convert to SI units. ## Note here Ca units do not enter, but ## units of CaMIN, CaMAX and ca_conc in fn expr should match. v = VMIN_here - vNegOffset CaMIN = float(concdep.attrib['min_conc']) CaMAX = float(concdep.attrib['max_conc']) CaNDIVS = 100 dCa = (CaMAX - CaMIN) / CaNDIVS ## CAREFUL!: tableA = [[0.0]*(CaNDIVS+1)]*(NDIVS_here+1) will not work! ## * does a shallow copy, same list will get repeated 200 times! ## Thus setting tableA[35][1] = 5.0 will set all rows, 1st col to 5.0!!!! tableA = [[0.0] * (CaNDIVS + 1) for i in range(NDIVS_here + 1)] tableB = [[0.0] * (CaNDIVS + 1) for i in range(NDIVS_here + 1)] for i in range(NDIVS_here + 1): Ca = CaMIN for j in range(CaNDIVS + 1): inf = self.inf(v, Ca) tau = self.tau(v, Ca) ## convert to SI (Tfactor) before writing to table ## qfactor is already in inf and tau tableA[i][j] = inf / tau / Tfactor tableB[i][j] = 1.0 / tau / Tfactor Ca += dCa v += dv_here ## Presently HHGate2D doesn't allow the setting of tables as 2D vectors directly moosegate.tableA = tableA moosegate.tableB = tableB ## set SI values inside MOOSE moosegate.xminA = VMIN_here * Vfactor moosegate.xmaxA = VMAX_here * Vfactor moosegate.xdivsA = NDIVS_here #moosegate.dxA = dv_here*Vfactor moosegate.yminA = CaMIN * concfactor moosegate.ymaxA = CaMAX * concfactor moosegate.ydivsA = CaNDIVS #moosegate.dyB = dCa*concfactor ## set SI values inside MOOSE moosegate.xminB = VMIN_here * Vfactor moosegate.xmaxB = VMAX_here * Vfactor moosegate.xdivsB = NDIVS_here #moosegate.dxB = dv_here*Vfactor moosegate.yminB = CaMIN * concfactor moosegate.ymaxB = CaMAX * concfactor moosegate.ydivsB = CaNDIVS
def readNeuroMLFromFile(self, filename, params={}, cellsDict={}): """ For the format of params required to tweak what cells are loaded, refer to the doc string of NetworkML.readNetworkMLFromFile(). Returns (populationDict,projectionDict), see doc string of NetworkML.readNetworkML() for details. """ pu.info("Loading neuroml file %s " % filename) moose.Neutral( '/library') # creates /library in MOOSE tree; elif present, wraps tree = ET.parse(filename) root_element = tree.getroot() self.model_dir = path.dirname(path.abspath(filename)) if 'lengthUnits' in list(root_element.attrib.keys()): self.lengthUnits = root_element.attrib['lengthUnits'] else: self.lengthUnits = 'micrometer' ## lots of gymnastics to check if temperature meta tag is present self.temperature = CELSIUS_default # gets replaced below if tag for temperature is present self.temperature_default = True for meta_property in root_element.findall('.//{' + meta_ns + '}property'): ## tag can be an attrib or an element if 'tag' in list(meta_property.attrib.keys()): # tag is an attrib tagname = meta_property.attrib['tag'] if 'temperature' in tagname: self.temperature = float(meta_property.attrib['value']) self.temperature_default = False else: # tag is a separate element tag = meta_property.find('.//{' + meta_ns + '}tag') tagname = tag.text if 'temperature' in tagname: ## value can be a tag or an element if 'value' in list( tag.attrib.keys()): # value is an attrib self.temperature = float(tag.attrib['value']) self.temperature_default = False else: # value is a separate element self.temperature = float( tag.find('.//{' + meta_ns + '}value').text) self.temperature_default = False if self.temperature_default: print(("Using default temperature of", self.temperature, "degrees Celsius.")) self.nml_params = { 'temperature': self.temperature, 'model_dir': self.model_dir, } #print "Loading channels and synapses into MOOSE /library ..." cmlR = ChannelML(self.nml_params) for channels in root_element.findall('.//{' + neuroml_ns + '}channels'): self.channelUnits = channels.attrib['units'] for channel in channels.findall('.//{' + cml_ns + '}channel_type'): ## ideally I should read in extra params ## from within the channel_type element and put those in also. ## Global params should override local ones. cmlR.readChannelML(channel, params=params, units=self.channelUnits) for synapse in channels.findall('.//{' + cml_ns + '}synapse_type'): cmlR.readSynapseML(synapse, units=self.channelUnits) for ionConc in channels.findall('.//{' + cml_ns + '}ion_concentration'): cmlR.readIonConcML(ionConc, units=self.channelUnits) #print "Loading cell definitions into MOOSE /library ..." mmlR = MorphML(self.nml_params) self.cellsDict = cellsDict for cells in root_element.findall('.//{' + neuroml_ns + '}cells'): for cell in cells.findall('.//{' + neuroml_ns + '}cell'): cellDict = mmlR.readMorphML(cell, params=params, lengthUnits=self.lengthUnits) self.cellsDict.update(cellDict) ## check if there are populations in this NML files, ## if not, it's a MorphML or ChannelML file, not NetworkML, so skip. if root_element.find('.//{'+neuroml_ns+'}populations') is None \ and root_element.find('.//{'+nml_ns+'}populations') is None: return (self.cellsDict, 'no populations (L3 NetworkML) found.') else: #print "Loading individual cells into MOOSE root ... " nmlR = NetworkML(self.nml_params) return nmlR.readNetworkML(root_element,self.cellsDict,\ params=params,lengthUnits=self.lengthUnits) ## cellsDict = { cellname: (segDict, cableDict), ... } # multiple cells ## where segDict = { segid1 : [ segname,(proximalx,proximaly,proximalz), ## (distalx,distaly,distalz),diameter,length,[potential_syn1, ... ] ] , ... } ## segname is "<name>_<segid>" because 1) guarantees uniqueness, ## & 2) later scripts obtain segid from the compartment's name! ## and cableDict = { cablegroupname : [campartment1name, compartment2name, ... ], ... } self.cellsDict = nmlR.cellSegmentDict
def readChannelML(self, channelElement, params={}, units="SI units"): ## I first calculate all functions assuming a consistent system of units. ## While filling in the A and B tables, I just convert to SI. ## Also convert gmax and Erev. if "Physiological Units" in units: # see pg 219 (sec 13.2) of Book of Genesis Vfactor = 1e-3 # V from mV Tfactor = 1e-3 # s from ms Gfactor = 1e1 # S/m^2 from mS/cm^2 concfactor = 1e6 # Mol = mol/m^-3 from mol/cm^-3 elif "SI Units" in units: Vfactor = 1.0 Tfactor = 1.0 Gfactor = 1.0 concfactor = 1.0 else: pu.fatal("Wrong units %s. Existing" % units) sys.exit(1) moose.Neutral("/library") # creates /library in MOOSE tree; elif present, wraps channel_name = channelElement.attrib["name"] if utils.neuroml_debug: pu.info("Loading channel %s into /library" % channel_name) IVrelation = channelElement.find("./{" + self.cml + "}current_voltage_relation") intfire = IVrelation.find("./{" + self.cml + "}integrate_and_fire") if intfire is not None: ## Below params need to be set while making an LIF compartment moosechannel = moose.Neutral("/library/" + channel_name) moosechannelval = moose.Mstring(moosechannel.path + "/vReset") moosechannelval.value = str(float(intfire.attrib["v_reset"]) * Vfactor) moosechannelval = moose.Mstring(moosechannel.path + "/thresh") moosechannelval.value = str(float(intfire.attrib["threshold"]) * Vfactor) moosechannelval = moose.Mstring(moosechannel.path + "/refracT") moosechannelval.value = str(float(intfire.attrib["t_refrac"]) * Tfactor) ## refracG is currently not supported by moose.LIF ## Confirm if g_refrac is a conductance density or not? ## assuming g_refrac is a conductance density below moosechannelval = moose.Mstring(moosechannel.path + "/refracG") moosechannelval.value = str(float(intfire.attrib["g_refrac"]) * Gfactor) ## create an Mstring saying this is an integrate_and_fire mechanism moosechannelval = moose.Mstring(moosechannel.path + "/integrate_and_fire") moosechannelval.value = "True" return concdep = IVrelation.find("./{" + self.cml + "}conc_dependence") if concdep is None: moosechannel = moose.HHChannel("/library/" + channel_name) else: moosechannel = moose.HHChannel2D("/library/" + channel_name) if IVrelation.attrib["cond_law"] == "ohmic": moosechannel.Gbar = float(IVrelation.attrib["default_gmax"]) * Gfactor moosechannel.Ek = float(IVrelation.attrib["default_erev"]) * Vfactor moosechannelIon = moose.Mstring(moosechannel.path + "/ion") moosechannelIon.value = IVrelation.attrib["ion"] if concdep is not None: moosechannelIonDependency = moose.Mstring(moosechannel.path + "/ionDependency") moosechannelIonDependency.value = concdep.attrib["ion"] nernstnote = IVrelation.find("./{" + utils.meta_ns + "}notes") if nernstnote is not None: ## the text in nernstnote is "Nernst,Cout=<float>,z=<int>" nernst_params = nernstnote.text.split(",") if nernst_params[0] == "Nernst": nernstMstring = moose.Mstring(moosechannel.path + "/nernst_str") nernstMstring.value = ( str(float(nernst_params[1].split("=")[1]) * concfactor) + "," + str(int(nernst_params[2].split("=")[1])) ) gates = IVrelation.findall("./{" + self.cml + "}gate") if len(gates) > 3: pu.fatal("Sorry! Maximum x, y, and z (three) gates are possible in MOOSE/Genesis") sys.exit() gate_full_name = ["gateX", "gateY", "gateZ"] # These are the names that MOOSE uses to create gates. ## if impl_prefs tag is present change VMIN, VMAX and NDIVS impl_prefs = channelElement.find("./{" + self.cml + "}impl_prefs") if impl_prefs is not None: table_settings = impl_prefs.find("./{" + self.cml + "}table_settings") ## some problem here... disable VMIN_here = float(table_settings.attrib["min_v"]) VMAX_here = float(table_settings.attrib["max_v"]) NDIVS_here = int(table_settings.attrib["table_divisions"]) dv_here = (VMAX_here - VMIN_here) / NDIVS_here else: ## default VMIN, VMAX and dv are in SI ## convert them to current calculation units used by channel definition ## while loading into tables, convert them back to SI VMIN_here = utils.VMIN / Vfactor VMAX_here = utils.VMAX / Vfactor NDIVS_here = utils.NDIVS dv_here = utils.dv / Vfactor offset = IVrelation.find("./{" + self.cml + "}offset") if offset is None: vNegOffset = 0.0 else: vNegOffset = float(offset.attrib["value"]) self.parameters = [] for parameter in channelElement.findall(".//{" + self.cml + "}parameter"): self.parameters.append((parameter.attrib["name"], float(parameter.attrib["value"]))) for num, gate in enumerate(gates): # if no q10settings tag, the q10factor remains 1.0 # if present but no gate attribute, then set q10factor # if there is a gate attribute, then set it only if gate attrib matches gate name self.q10factor = 1.0 self.gate_name = gate.attrib["name"] for q10settings in IVrelation.findall("./{" + self.cml + "}q10_settings"): ## self.temperature from neuro.utils if "gate" in q10settings.attrib: if q10settings.attrib["gate"] == self.gate_name: self.setQ10(q10settings) break else: self.setQ10(q10settings) ############### HHChannel2D crashing on setting Xpower! #### temperamental! If you print something before, it gives cannot creategate from copied channel, else crashes ## Setting power first. This is necessary because it also ## initializes the gate's internal data structures as a side ## effect. Alternatively, gates can be initialized explicitly ## by calling HHChannel.createGate(). gate_power = float(gate.get("instances")) if num == 0: moosechannel.Xpower = gate_power if concdep is not None: moosechannel.Xindex = "VOLT_C1_INDEX" elif num == 1: moosechannel.Ypower = gate_power if concdep is not None: moosechannel.Yindex = "VOLT_C1_INDEX" elif num == 2: moosechannel.Zpower = gate_power if concdep is not None: moosechannel.Zindex = "VOLT_C1_INDEX" ## Getting handle to gate using the gate's path. gate_path = moosechannel.path + "/" + gate_full_name[num] if concdep is None: moosegate = moose.HHGate(gate_path) ## set SI values inside MOOSE moosegate.min = VMIN_here * Vfactor moosegate.max = VMAX_here * Vfactor moosegate.divs = NDIVS_here ## V.IMP to get smooth curves, else even with 3000 divisions ## there are sudden transitions. moosegate.useInterpolation = True else: moosegate = moose.HHGate2D(gate_path) ##### If alpha and beta functions exist, make them here for transition in gate.findall("./{" + self.cml + "}transition"): ## make python functions with names of transitions... fn_name = transition.attrib["name"] ## I assume that transitions if present are called alpha and beta ## for forward and backward transitions... if fn_name in ["alpha", "beta"]: self.make_cml_function(transition, fn_name, concdep) else: pu.fatal("Unsupported transition %s" % fn_name) sys.exit() time_course = gate.find("./{" + self.cml + "}time_course") ## tau is divided by self.q10factor in make_function() ## thus, it gets divided irrespective of <time_course> tag present or not. if time_course is not None: self.make_cml_function(time_course, "tau", concdep) steady_state = gate.find("./{" + self.cml + "}steady_state") if steady_state is not None: self.make_cml_function(steady_state, "inf", concdep) if concdep is None: ca_name = "" # no Ca dependence else: ca_name = "," + concdep.attrib["variable_name"] # Ca dependence ## Create tau() and inf() if not present, from alpha() and beta() for fn_element, fn_name, fn_expr in [ (time_course, "tau", "1/(alpha+beta)"), (steady_state, "inf", "alpha/(alpha+beta)"), ]: ## put in args for alpha and beta, could be v and Ca dep. expr_string = fn_expr.replace("alpha", "self.alpha(v" + ca_name + ")") expr_string = expr_string.replace("beta", "self.beta(v" + ca_name + ")") ## if time_course/steady_state are not present, ## then alpha annd beta transition elements should be present, and fns created. if fn_element is None: self.make_function(fn_name, "generic", expr_string=expr_string, concdep=concdep) ## non Ca dependent channel if concdep is None: ## while calculating, use the units used in xml defn, ## while filling in table, I convert to SI units. v0 = VMIN_here - vNegOffset n_entries = NDIVS_here + 1 tableA = [0.0] * n_entries tableB = [0.0] * n_entries for i in range(n_entries): v = v0 + i * dv_here inf = self.inf(v) tau = self.tau(v) ## convert to SI before writing to table ## qfactor is already in inf and tau tableA[i] = inf / tau / Tfactor tableB[i] = 1.0 / tau / Tfactor moosegate.tableA = tableA moosegate.tableB = tableB ## Ca dependent channel else: ## UNITS: while calculating, use the units used in xml defn, ## while filling in table, I convert to SI units. ## Note here Ca units do not enter, but ## units of CaMIN, CaMAX and ca_conc in fn expr should match. v = VMIN_here - vNegOffset CaMIN = float(concdep.attrib["min_conc"]) CaMAX = float(concdep.attrib["max_conc"]) CaNDIVS = 100 dCa = (CaMAX - CaMIN) / CaNDIVS ## CAREFUL!: tableA = [[0.0]*(CaNDIVS+1)]*(NDIVS_here+1) will not work! ## * does a shallow copy, same list will get repeated 200 times! ## Thus setting tableA[35][1] = 5.0 will set all rows, 1st col to 5.0!!!! tableA = [[0.0] * (CaNDIVS + 1) for i in range(NDIVS_here + 1)] tableB = [[0.0] * (CaNDIVS + 1) for i in range(NDIVS_here + 1)] for i in range(NDIVS_here + 1): Ca = CaMIN for j in range(CaNDIVS + 1): inf = self.inf(v, Ca) tau = self.tau(v, Ca) ## convert to SI (Tfactor) before writing to table ## qfactor is already in inf and tau tableA[i][j] = inf / tau / Tfactor tableB[i][j] = 1.0 / tau / Tfactor Ca += dCa v += dv_here ## Presently HHGate2D doesn't allow the setting of tables as 2D vectors directly moosegate.tableA = tableA moosegate.tableB = tableB ## set SI values inside MOOSE moosegate.xminA = VMIN_here * Vfactor moosegate.xmaxA = VMAX_here * Vfactor moosegate.xdivsA = NDIVS_here # moosegate.dxA = dv_here*Vfactor moosegate.yminA = CaMIN * concfactor moosegate.ymaxA = CaMAX * concfactor moosegate.ydivsA = CaNDIVS # moosegate.dyB = dCa*concfactor ## set SI values inside MOOSE moosegate.xminB = VMIN_here * Vfactor moosegate.xmaxB = VMAX_here * Vfactor moosegate.xdivsB = NDIVS_here # moosegate.dxB = dv_here*Vfactor moosegate.yminB = CaMIN * concfactor moosegate.ymaxB = CaMAX * concfactor moosegate.ydivsB = CaNDIVS
def readNeuroMLFromFile(self,filename,params={},cellsDict={}): """ For the format of params required to tweak what cells are loaded, refer to the doc string of NetworkML.readNetworkMLFromFile(). Returns (populationDict,projectionDict), see doc string of NetworkML.readNetworkML() for details. """ pu.info("Loading neuroml file %s " % filename) moose.Neutral('/library') # creates /library in MOOSE tree; elif present, wraps tree = ET.parse(filename) root_element = tree.getroot() self.model_dir = path.dirname( path.abspath( filename ) ) if 'lengthUnits' in list(root_element.attrib.keys()): self.lengthUnits = root_element.attrib['lengthUnits'] else: self.lengthUnits = 'micrometer' ## lots of gymnastics to check if temperature meta tag is present self.temperature = CELSIUS_default # gets replaced below if tag for temperature is present self.temperature_default = True for meta_property in root_element.findall('.//{'+meta_ns+'}property'): ## tag can be an attrib or an element if 'tag' in list(meta_property.attrib.keys()): # tag is an attrib tagname = meta_property.attrib['tag'] if 'temperature' in tagname: self.temperature = float(meta_property.attrib['value']) self.temperature_default = False else: # tag is a separate element tag = meta_property.find('.//{'+meta_ns+'}tag') tagname = tag.text if 'temperature' in tagname: ## value can be a tag or an element if 'value' in list(tag.attrib.keys()): # value is an attrib self.temperature = float(tag.attrib['value']) self.temperature_default = False else: # value is a separate element self.temperature = float(tag.find('.//{'+meta_ns+'}value').text) self.temperature_default = False if self.temperature_default: print(("Using default temperature of", self.temperature,"degrees Celsius.")) self.nml_params = { 'temperature':self.temperature, 'model_dir':self.model_dir, } #print "Loading channels and synapses into MOOSE /library ..." cmlR = ChannelML(self.nml_params) for channels in root_element.findall('.//{'+neuroml_ns+'}channels'): self.channelUnits = channels.attrib['units'] for channel in channels.findall('.//{'+cml_ns+'}channel_type'): ## ideally I should read in extra params ## from within the channel_type element and put those in also. ## Global params should override local ones. cmlR.readChannelML(channel,params=params,units=self.channelUnits) for synapse in channels.findall('.//{'+cml_ns+'}synapse_type'): cmlR.readSynapseML(synapse,units=self.channelUnits) for ionConc in channels.findall('.//{'+cml_ns+'}ion_concentration'): cmlR.readIonConcML(ionConc,units=self.channelUnits) #print "Loading cell definitions into MOOSE /library ..." mmlR = MorphML(self.nml_params) self.cellsDict = cellsDict for cells in root_element.findall('.//{'+neuroml_ns+'}cells'): for cell in cells.findall('.//{'+neuroml_ns+'}cell'): cellDict = mmlR.readMorphML(cell,params=params,lengthUnits=self.lengthUnits) self.cellsDict.update(cellDict) ## check if there are populations in this NML files, ## if not, it's a MorphML or ChannelML file, not NetworkML, so skip. if root_element.find('.//{'+neuroml_ns+'}populations') is None \ and root_element.find('.//{'+nml_ns+'}populations') is None: return (self.cellsDict,'no populations (L3 NetworkML) found.') else: #print "Loading individual cells into MOOSE root ... " nmlR = NetworkML(self.nml_params) return nmlR.readNetworkML(root_element,self.cellsDict,\ params=params,lengthUnits=self.lengthUnits) ## cellsDict = { cellname: (segDict, cableDict), ... } # multiple cells ## where segDict = { segid1 : [ segname,(proximalx,proximaly,proximalz), ## (distalx,distaly,distalz),diameter,length,[potential_syn1, ... ] ] , ... } ## segname is "<name>_<segid>" because 1) guarantees uniqueness, ## & 2) later scripts obtain segid from the compartment's name! ## and cableDict = { cablegroupname : [campartment1name, compartment2name, ... ], ... } self.cellsDict = nmlR.cellSegmentDict
def readMorphML(self,cell,params={},lengthUnits="micrometer"): """ returns cellDict = { cellname: (segDict, cableDict) } # note: single cell only where segDict = { segid1 : [ segname,(proximalx,proximaly,proximalz), (distalx,distaly,distalz),diameter,length,[potential_syn1, ... ] ] , ... } segname is "<name>_<segid>" because 1) guarantees uniqueness, & 2) later scripts obtain segid from the compartment's name! and cableDict = { cablegroupname : [campartment1name, compartment2name, ... ], ... }. params is dict which can contain, combineSegments and/or createPotentialSynapses, both boolean. """ if lengthUnits in ['micrometer','micron']: self.length_factor = 1e-6 else: self.length_factor = 1.0 cellname = cell.attrib["name"] moose.Neutral('/library') # creates /library in MOOSE tree; elif present, wraps pu.info("Loading cell %s into /library ." % cellname) #~ moosecell = moose.Cell('/library/'+cellname) #using moose Neuron class - in previous version 'Cell' class Chaitanya moosecell = moose.Neuron('/library/'+cellname) self.cellDictBySegmentId[cellname] = [moosecell,{}] self.cellDictByCableId[cellname] = [moosecell,{}] self.segDict = {} if 'combineSegments' in list(params.keys()): self.combineSegments = params['combineSegments'] else: self.combineSegments = False if 'createPotentialSynapses' in list(params.keys()): self.createPotentialSynapses = params['createPotentialSynapses'] else: self.createPotentialSynapses = False pu.info("readMorphML using combineSegments = %s" % self.combineSegments) ############################################### #### load cablegroups into a dictionary self.cablegroupsDict = {} self.cablegroupsInhomoparamsDict = {} ## Two ways of specifying cablegroups in neuroml 1.x ## <cablegroup>s with list of <cable>s cablegroups = cell.findall(".//{"+self.mml+"}cablegroup") for cablegroup in cablegroups: cablegroupname = cablegroup.attrib['name'] self.cablegroupsDict[cablegroupname] = [] self.cablegroupsInhomoparamsDict[cablegroupname] = [] for cable in cablegroup.findall(".//{"+self.mml+"}cable"): cableid = cable.attrib['id'] self.cablegroupsDict[cablegroupname].append(cableid) # parse inhomogenous_params for inhomogeneous_param in cablegroup.findall(".//{"+self.mml+"}inhomogeneous_param"): metric = inhomogeneous_param.find(".//{"+self.mml+"}metric") if metric.text == 'Path Length from root': inhomoparamname = inhomogeneous_param.attrib['name'] inhomoparamvar = inhomogeneous_param.attrib['variable'] self.cablegroupsInhomoparamsDict[cablegroupname].append(\ (inhomoparamname,inhomoparamvar)) else: pu.warn('Only "Path Length from root" metric is supported currently, ignoring '+metric.text) ## <cable>s with list of <meta:group>s cables = cell.findall(".//{"+self.mml+"}cable") for cable in cables: cableid = cable.attrib['id'] cablegroups = cable.findall(".//{"+self.meta+"}group") for cablegroup in cablegroups: cablegroupname = cablegroup.text if cablegroupname in list(self.cablegroupsDict.keys()): self.cablegroupsDict[cablegroupname].append(cableid) else: self.cablegroupsDict[cablegroupname] = [cableid] ################################################### ## load all mechanisms in this cell into /library for later copying ## set which compartments have integrate_and_fire mechanism self.intFireCableIds = {} # dict with keys as Compartments/cableIds which are IntFire # with mechanismnames as values for mechanism in cell.findall(".//{"+self.bio+"}mechanism"): mechanismname = mechanism.attrib["name"] passive = False if "passive_conductance" in mechanism.attrib: if mechanism.attrib['passive_conductance'] in ["true",'True','TRUE']: passive = True if not passive: ## if channel does not exist in library load it from xml file if not moose.exists("/library/"+mechanismname): pu.info("Loading mechanism %s into library." % mechanismname) cmlR = ChannelML(self.nml_params) model_filename = mechanismname+'.xml' model_path = neuroml_utils.find_first_file(model_filename,self.model_dir) if model_path is not None: cmlR.readChannelMLFromFile(model_path) else: raise IOError( 'For mechanism {0}: files {1} not found under {2}.'.format( mechanismname, model_filename, self.model_dir) ) ## set those compartments to be LIF for which ## any integrate_and_fire parameter is set if not moose.exists( "/library/"+mechanismname): print "Mechanism doesn't exist: ", mechanismname moose.le( '/library' ) moosemech = moose.element("/library/"+mechanismname) if moose.exists(moosemech.path+"/integrate_and_fire"): mooseIaF = moose.element(moosemech.path+"/integrate_and_fire") # Mstring if mooseIaF.value in ['true','True','TRUE']: mech_params = mechanism.findall(".//{"+self.bio+"}parameter") for parameter in mech_params: parametername = parameter.attrib['name'] ## check for the integrate_and_fire parameters if parametername in ['threshold', 't_refrac', 'v_reset','g_refrac']: for group in parameter.findall(".//{"+self.bio+"}group"): cablegroupname = group.text if cablegroupname == 'all': self.intFireCableIds = {'all':mechanismname} break else: for cableid in self.cablegroupsDict[cablegroupname]: ## only one intfire mechanism is allowed in a cable ## the last one parsed will override others self.intFireCableIds[cableid] = mechanismname if 'all' in list(self.intFireCableIds.keys()): break ############################################################ #### load morphology and connections between compartments ## Many neurons exported from NEURON have multiple segments in a section ## If self.combineSegments = True, ## then combine those segments into one Compartment / section ## for combining, assume segments of a compartment/section are in increasing order ## and assume all segments of a compartment/section have the same cableid ## findall() returns elements in document order: running_cableid = '' running_segid = '' running_comp = None running_diameter = 0.0 running_dia_nums = 0 segments = cell.findall(".//{"+self.mml+"}segment") segmentstotal = len(segments) for segnum,segment in enumerate(segments): segmentname = segment.attrib['name'] ## cable is an optional attribute. WARNING: Here I assume it is always present. cableid = segment.attrib['cable'] segmentid = segment.attrib['id'] ## if old cableid still running AND self.combineSegments == True, ## then don't start a new compartment, skip to next segment if cableid == running_cableid and self.combineSegments: self.cellDictBySegmentId[cellname][1][segmentid] = running_comp proximal = segment.find('./{'+self.mml+'}proximal') if proximal is not None: running_diameter += float(proximal.attrib["diameter"]) * self.length_factor running_dia_nums += 1 distal = segment.find('./{'+self.mml+'}distal') if distal is not None: running_diameter += float(distal.attrib["diameter"]) * self.length_factor running_dia_nums += 1 ## if (self.combineSegments and new cableid starts) or if not self.combineSegments, ## then start a new compartment else: ## Create a new compartment ## the moose "hsolve" method assumes compartments to be ## asymmetric compartments and symmetrizes them ## but that is not what we want when translating ## from Neuron which has only symcompartments -- so be careful! ## Check if integrate_and_fire mechanism is present, ## if so use LIF instead of Compartment moosecompname = segmentname+'_'+segmentid # just segmentname is NOT unique # eg: mitral bbmit exported from NEURON moosecomppath = moosecell.path+'/'+moosecompname IntFireIds = list(self.intFireCableIds.keys()) mechanismname = None if 'all' in IntFireIds: mechanismname = self.intFireCableIds['all'] if cableid in IntFireIds: mechanismname = self.intFireCableIds[cableid] if mechanismname is not None: # this cableid is an intfire ## create LIF (subclass of Compartment) and set to default values moosecomp = moose.LIF(moosecomppath) moosechannel = moose.Neutral('/library/'+mechanismname) moosechannelval = moose.Mstring(moosechannel.path+'/vReset') moosecomp.vReset = moosechannelval.value moosechannelval = moose.Mstring(moosechannel.path+'/thresh') moosecomp.thresh = moosechannelval.value moosechannelval = moose.Mstring(moosechannel.path+'/refracT') moosecomp.refractoryPeriod = moosechannelval.value ## refracG is currently not supported by moose.LIF ## when you implement it, check if refracG or g_refrac ## is a conductance density or a conductance, I think the former #moosechannelval = moose.Mstring(moosechannel.path+'/refracG') else: moosecomp = moose.Compartment(moosecomppath) self.cellDictBySegmentId[cellname][1][segmentid] = moosecomp ## cables are grouped and mechanism densities are set for cablegroups later. ## hence I will need to refer to segment according to which cable it belongs to. ## if combineSegments is False, there can be multiple segments per cable, ## so make array of compartments for cellDictByCableId[cellname][1][cableid] if cableid in list(self.cellDictByCableId[cellname][1].keys()): self.cellDictByCableId[cellname][1][cableid].append(moosecomp) else: self.cellDictByCableId[cellname][1][cableid] = [moosecomp] running_cableid = cableid running_segid = segmentid running_comp = moosecomp running_diameter = 0.0 running_dia_nums = 0 if 'parent' in segment.attrib: parentid = segment.attrib['parent'] # I assume the parent is created before the child # so that I can immediately connect the child. parent = self.cellDictBySegmentId[cellname][1][parentid] ## It is always assumed that axial of parent is connected to raxial of moosesegment ## THIS IS WHAT GENESIS readcell() DOES!!! UNLIKE NEURON! ## THIS IS IRRESPECTIVE OF WHETHER PROXIMAL x,y,z OF PARENT = PROXIMAL x,y,z OF CHILD. ## THIS IS ALSO IRRESPECTIVE OF fraction_along_parent SPECIFIED IN CABLE! ## THUS THERE WILL BE NUMERICAL DIFFERENCES BETWEEN MOOSE/GENESIS and NEURON. ## moosesegment sends Ra and Vm to parent, parent sends only Vm ## actually for symmetric compartment, both parent and moosesegment require each other's Ra/2, ## but axial and raxial just serve to distinguish ends. moose.connect(parent,'axial',moosecomp,'raxial') else: parent = None proximal = segment.find('./{'+self.mml+'}proximal') if proximal is None: # If proximal tag is not present, # then parent attribute MUST be present in the segment tag! ## if proximal is not present, then ## by default the distal end of the parent is the proximal end of the child moosecomp.x0 = parent.x moosecomp.y0 = parent.y moosecomp.z0 = parent.z else: moosecomp.x0 = float(proximal.attrib["x"])*self.length_factor moosecomp.y0 = float(proximal.attrib["y"])*self.length_factor moosecomp.z0 = float(proximal.attrib["z"])*self.length_factor running_diameter += float(proximal.attrib["diameter"]) * self.length_factor running_dia_nums += 1 distal = segment.find('./{'+self.mml+'}distal') if distal is not None: running_diameter += float(distal.attrib["diameter"]) * self.length_factor running_dia_nums += 1 ## finished creating new compartment ## Update the end position, diameter and length, and segDict of this comp/cable/section ## with each segment that is part of this cable (assumes contiguous segments in xml). ## This ensures that we don't have to do any 'closing ceremonies', ## if a new cable is encoutered in next iteration. if distal is not None: running_comp.x = float(distal.attrib["x"])*self.length_factor running_comp.y = float(distal.attrib["y"])*self.length_factor running_comp.z = float(distal.attrib["z"])*self.length_factor ## Set the compartment diameter as the average diameter of all the segments in this section running_comp.diameter = running_diameter / float(running_dia_nums) ## Set the compartment length running_comp.length = math.sqrt((running_comp.x-running_comp.x0)**2+\ (running_comp.y-running_comp.y0)**2+(running_comp.z-running_comp.z0)**2) ## NeuroML specs say that if (x0,y0,z0)=(x,y,z), then round compartment e.g. soma. ## In Moose set length = dia to give same surface area as sphere of dia. if running_comp.length == 0.0: running_comp.length = running_comp.diameter ## Set the segDict ## the empty list at the end below will get populated ## with the potential synapses on this segment, in function set_compartment_param(..) self.segDict[running_segid] = [running_comp.name,\ (running_comp.x0,running_comp.y0,running_comp.z0),\ (running_comp.x,running_comp.y,running_comp.z),\ running_comp.diameter,running_comp.length,[]] if neuroml_utils.neuroml_debug: pu.info('Set up compartment/section %s' % running_comp.name) ############################################### #### load biophysics into the compartments biophysics = cell.find(".//{"+self.neuroml+"}biophysics") if biophysics is not None: ## see pg 219 (sec 13.2) of Book of Genesis for Physiological Units if biophysics.attrib["units"] == 'Physiological Units': CMfactor = 1e-2 # F/m^2 from microF/cm^2 Cfactor = 1e-6 # F from microF RAfactor = 1e1 # Ohm*m from KOhm*cm RMfactor = 1e-1 # Ohm*m^2 from KOhm*cm^2 Rfactor = 1e-3 # Ohm from KOhm Efactor = 1e-3 # V from mV Gfactor = 1e1 # S/m^2 from mS/cm^2 Ifactor = 1e-6 # A from microA Tfactor = 1e-3 # s from ms else: CMfactor = 1.0 Cfactor = 1.0 RAfactor = 1.0 RMfactor = 1.0 Rfactor = 1.0 Efactor = 1.0 Gfactor = 1.0 Ifactor = 1.0 Tfactor = 1.0 spec_capacitance = cell.find(".//{"+self.bio+"}spec_capacitance") for parameter in spec_capacitance.findall(".//{"+self.bio+"}parameter"): self.set_group_compartment_param(cell, cellname, parameter,\ 'CM', float(parameter.attrib["value"])*CMfactor, self.bio) spec_axial_resitance = cell.find(".//{"+self.bio+"}spec_axial_resistance") for parameter in spec_axial_resitance.findall(".//{"+self.bio+"}parameter"): self.set_group_compartment_param(cell, cellname, parameter,\ 'RA', float(parameter.attrib["value"])*RAfactor, self.bio) init_memb_potential = cell.find(".//{"+self.bio+"}init_memb_potential") for parameter in init_memb_potential.findall(".//{"+self.bio+"}parameter"): self.set_group_compartment_param(cell, cellname, parameter,\ 'initVm', float(parameter.attrib["value"])*Efactor, self.bio) chan_distrib = [] # the list for moose to parse inhomogeneous params (filled below) for mechanism in cell.findall(".//{"+self.bio+"}mechanism"): mechanismname = mechanism.attrib["name"] passive = False if "passive_conductance" in mechanism.attrib: if mechanism.attrib['passive_conductance'] in ["true",'True','TRUE']: passive = True pu.info("Loading mechanism %s " % mechanismname) ## ONLY creates channel if at least one parameter (like gmax) is specified in the xml ## Neuroml does not allow you to specify all default values. ## However, granule cell example in neuroconstruct has Ca ion pool without ## a parameter, applying default values to all compartments! mech_params = mechanism.findall(".//{"+self.bio+"}parameter") ## if no params, apply all default values to all compartments if len(mech_params) == 0: for compartment_list in list(self.cellDictByCableId[cellname][1].values()): for compartment in compartment_list: self.set_compartment_param(compartment,None,'default',mechanismname) ## if params are present, apply params to specified cable/compartment groups for parameter in mech_params: parametername = parameter.attrib['name'] if passive: if parametername in ['gmax']: self.set_group_compartment_param(cell, cellname, parameter,\ 'RM', RMfactor*1.0/float(parameter.attrib["value"]), self.bio) elif parametername in ['e','erev']: self.set_group_compartment_param(cell, cellname, parameter,\ 'Em', Efactor*float(parameter.attrib["value"]), self.bio) elif parametername in ['inject']: self.set_group_compartment_param(cell, cellname, parameter,\ 'inject', Ifactor*float(parameter.attrib["value"]), self.bio) else: pu.warn(["Yo programmer of MorphML! You didn't" , " implement parameter %s " % parametername , " in mechanism %s " % mechanismname ] ) else: if parametername in ['gmax']: gmaxval = float(eval(parameter.attrib["value"],{"__builtins__":None},{})) self.set_group_compartment_param(cell, cellname, parameter,\ 'Gbar', Gfactor*gmaxval, self.bio, mechanismname) elif parametername in ['e','erev']: self.set_group_compartment_param(cell, cellname, parameter,\ 'Ek', Efactor*float(parameter.attrib["value"]), self.bio, mechanismname) elif parametername in ['depth']: # has to be type Ion Concentration! self.set_group_compartment_param(cell, cellname, parameter,\ 'thick', self.length_factor*float(parameter.attrib["value"]),\ self.bio, mechanismname) elif parametername in ['v_reset']: self.set_group_compartment_param(cell, cellname, parameter,\ 'v_reset', Efactor*float(parameter.attrib["value"]),\ self.bio, mechanismname) elif parametername in ['threshold']: self.set_group_compartment_param(cell, cellname, parameter,\ 'threshold', Efactor*float(parameter.attrib["value"]),\ self.bio, mechanismname) elif parametername in ['t_refrac']: self.set_group_compartment_param(cell, cellname, parameter,\ 't_refrac', Tfactor*float(parameter.attrib["value"]),\ self.bio, mechanismname) else: pu.warn(["Yo programmer of MorphML import! You didn't" , " implement parameter %s " % parametername , " in mechanism %s " % mechanismname ] ) ## variable parameters: ## varying with: ## p, g, L, len, dia ## p: path distance from soma, measured along dendrite, in metres. ## g: geometrical distance from soma, in metres. ## L: electrotonic distance (# of lambdas) from soma, along dend. No units. ## len: length of compartment, in metres. ## dia: for diameter of compartment, in metres. var_params = mechanism.findall(".//{"+self.bio+"}variable_parameter") if len(var_params) > 0: ## if variable params are present ## and use MOOSE to apply the variable formula for parameter in var_params: parametername = parameter.attrib['name'] cablegroupstr4moose = "" ## the neuroml spec says there should be a single group in a variable_parameter ## of course user can always have multiple variable_parameter tags, ## if user wants multiple groups conforming to neuroml specs. group = parameter.find(".//{"+self.bio+"}group") cablegroupname = group.text if cablegroupname == 'all': cablegroupstr4moose = "#" else: for cableid in self.cablegroupsDict[cablegroupname]: for compartment in self.cellDictByCableId[cellname][1][cableid]: cablegroupstr4moose += "#"+compartment.name+"#," if cablegroupstr4moose[-1] == ',': cablegroupstr4moose = cablegroupstr4moose[:-1] # remove last comma inhomo_value = parameter.find(".//{"+self.bio+"}inhomogeneous_value") inhomo_value_name = inhomo_value.attrib['param_name'] inhomo_value_value = inhomo_value.attrib['value'] if parametername == 'gmax': inhomo_eqn = '('+inhomo_value_value+')*'+str(Gfactor) # careful about physiol vs SI units else: inhomo_eqn = inhomo_value_value pu.warn('Physiol. vs SI units translation not' ' implemented for parameter '+parametername+ 'in channel '+mechanismname)+'. Use SI units' 'or ask for implementation.' chan_distrib.extend((mechanismname,cablegroupstr4moose,parametername,inhomo_eqn,"")) # use extend, not append, moose wants it this way ## get mooose to parse the variable parameter gmax channel distributions #pu.info("Some channel parameters distributed as per "+str(chan_distrib)) moosecell.channelDistribution = chan_distrib #### Connect the Ca pools and channels #### Am connecting these at the very end so that all channels and pools have been created #### Note: this function is in moose.utils not moose.neuroml.utils ! for compartment_list in list(self.cellDictByCableId[cellname][1].values()): moose_utils.connect_CaConc(compartment_list,\ self.temperature+neuroml_utils.ZeroCKelvin) # temperature should be in Kelvin for Nernst ########################################################## #### load connectivity / synapses into the compartments connectivity = cell.find(".//{"+self.neuroml+"}connectivity") if connectivity is not None: for potential_syn_loc in cell.findall(".//{"+self.nml+"}potential_syn_loc"): if 'synapse_direction' in list(potential_syn_loc.attrib.keys()): if potential_syn_loc.attrib['synapse_direction'] in ['post','preAndOrPost']: self.set_group_compartment_param(cell, cellname, potential_syn_loc,\ 'synapse_type', potential_syn_loc.attrib['synapse_type'],\ self.nml, mechanismname='synapse') if potential_syn_loc.attrib['synapse_direction'] in ['pre','preAndOrPost']: self.set_group_compartment_param(cell, cellname, potential_syn_loc,\ 'spikegen_type', potential_syn_loc.attrib['synapse_type'],\ self.nml, mechanismname='spikegen') ########################################################## #### annotate each compartment with the cablegroups it belongs to self.cableDict = {} for cablegroupname in list(self.cablegroupsDict.keys()): comp_list = [] for cableid in self.cablegroupsDict[cablegroupname]: for compartment in self.cellDictByCableId[cellname][1][cableid]: cableStringPath = compartment.path+'/cable_groups' cableString = moose.Mstring(cableStringPath) if cableString.value == '': cableString.value += cablegroupname else: cableString.value += ',' + cablegroupname comp_list.append(compartment.name) self.cableDict[cablegroupname] = comp_list pu.info("Finished loading into library, cell: %s " % cellname) return {cellname:(self.segDict,self.cableDict)}
def set_compartment_param(self, compartment, name, value, mechanismname): """ Set the param for the compartment depending on name and mechanismname. """ if name == 'CM': compartment.Cm = value*math.pi*compartment.diameter*compartment.length elif name == 'RM': compartment.Rm = value/(math.pi*compartment.diameter*compartment.length) elif name == 'RA': compartment.Ra = value*compartment.length/(math.pi*(compartment.diameter/2.0)**2) elif name == 'Em': compartment.Em = value elif name == 'initVm': compartment.initVm = value elif name == 'inject': # this reader converts to SI pu.info("Comparment %s inject %s A." % (compartment.name, value)) compartment.inject = value elif name == 'v_reset': compartment.vReset = value # compartment is a moose.LIF instance (intfire) elif name == 'threshold': compartment.thresh = value # compartment is a moose.LIF instance (intfire) elif name == 't_refrac': compartment.refractoryPeriod = value # compartment is a moose.LIF instance (intfire) elif name == 'g_refrac': pu.info("SORRY, current moose.LIF doesn't support g_refrac.") elif mechanismname is 'synapse': # synapse being added to the compartment ## these are potential locations, we do not actually make synapses, ## unless the user has explicitly asked for it if self.createPotentialSynapses: syn_name = value if not moose.exists(compartment.path+'/'+syn_name): make_new_synapse(syn_name, compartment, syn_name, self.nml_params) ## I assume below that compartment name has _segid at its end segid = string.split(compartment.name,'_')[-1] # get segment id from compartment name self.segDict[segid][5].append(value) elif mechanismname is 'spikegen': # spikegen being added to the compartment ## these are potential locations, we do not actually make the spikegens. ## spikegens for different synapses can have different thresholds, ## hence include synapse_type in its name ## value contains name of synapse i.e. synapse_type #spikegen = moose.SpikeGen(compartment.path+'/'+value+'_spikegen') #moose.connect(compartment,"VmSrc",spikegen,"Vm") pass ## previous were mechanism that don't need a ChannelML definition ## including integrate_and_fire (I ignore the ChannelML definition) ## thus integrate_and_fire mechanism default values cannot be used ## i.e. nothing needed in /library, but below mechanisms need. elif mechanismname is not None: ## if mechanism is not present in compartment, deep copy from library ## all mechanisms have been loaded into the library earlier if not moose.exists(compartment.path+'/'+mechanismname): neutralObj = moose.element("/library/"+mechanismname) # gives error if not present if 'CaConc' == neutralObj.className: # Ion concentration pool libcaconc = moose.CaConc("/library/"+mechanismname) ## deep copies the library caconc under the compartment caconc = moose.copy(libcaconc,compartment,mechanismname) caconc = moose.CaConc(caconc) ## CaConc connections are made later using connect_CaConc() ## Later, when calling connect_CaConc, ## B is set for caconc based on thickness of Ca shell and compartment l and dia ## OR based on the Mstring phi under CaConc path. channel = None elif 'HHChannel2D' == neutralObj.className : ## HHChannel2D libchannel = moose.HHChannel2D("/library/"+mechanismname) ## deep copies the library channel under the compartment channel = moose.copy(libchannel,compartment,mechanismname) channel = moose.HHChannel2D(channel) moose.connect(channel,'channel',compartment,'channel') elif 'HHChannel' == neutralObj.className : ## HHChannel libchannel = moose.HHChannel("/library/"+mechanismname) ## deep copies the library channel under the compartment channel = moose.copy(libchannel,compartment,mechanismname) channel = moose.HHChannel(channel) moose.connect(channel,'channel',compartment,'channel') ## if mechanism is present in compartment, just wrap it else: neutralObj = moose.Neutral(compartment.path+'/'+mechanismname) if 'CaConc' == neutralObj.className: # Ion concentration pool caconc = moose.CaConc(compartment.path+'/'+mechanismname) # wraps existing channel channel = None elif 'HHChannel2D' == neutralObj.className : ## HHChannel2D channel = moose.HHChannel2D(compartment.path+'/'+mechanismname) # wraps existing channel elif 'HHChannel' == neutralObj.className : ## HHChannel channel = moose.HHChannel(compartment.path+'/'+mechanismname) # wraps existing channel if name == 'Gbar': if channel is None: # if CaConc, neuroConstruct uses gbar for thickness or phi ## If child Mstring 'phi' is present, set gbar as phi ## BUT, value has been multiplied by Gfactor as a Gbar, ## SI or physiological not known here, ## ignoring Gbar for CaConc, instead of passing units here child = moose_utils.get_child_Mstring(caconc,'phi') if child is not None: #child.value = value pass else: #caconc.thick = value pass else: # if ion channel, usual Gbar channel.Gbar = value*math.pi*compartment.diameter*compartment.length elif name == 'Ek': channel.Ek = value elif name == 'thick': # thick seems to be NEURON's extension to NeuroML level 2. caconc.thick = value ## JUST THIS WILL NOT DO - HAVE TO SET B based on this thick! ## Later, when calling connect_CaConc, ## B is set for caconc based on thickness of Ca shell and compartment l and dia. ## OR based on the Mstring phi under CaConc path. if neuroml_utils.neuroml_debug: pu.info("Setting %s for comparment %s to %s" % (name, compartment.path, value))