def addControlLines(self): """Add .CONTROL lines to spice netlist """ self.spiceText.append("\n\n** Simulation etc. ") self.spiceText.append( "\n.CONTROL" "\nset hcopydevtype=postscript" "\nset hcopypscolor=true" "\nset color0 = white ;background" "\nset color1 = black ;text and grid" "\nset color2 = rgb:f/0/0 ;vector0" "\nset color3 = rgb:0/f/0 ;vector1" "\nset color3 = rgb:0/0/f ;vector2" "\nop" ) # Here we get the time of simulation and total steps done by moose. assert self.clock, "Main clock is not foind" simTime = self.clock.runTime dt = self.clock.dt self.spiceText.append("TRAN {0} {1}".format(dt, simTime)) plotLine = "HARDCOPY {}.ps ".format(self.outputFile) for t in self.tables: targets = t.neighbors['requestOut'] for tgt in targets: if tgt.className == 'PulseGen': debug.dump("INFO" , "Stimulus pulse are not plotted by defualt." ) continue plotLine += "V({}) ".format(self.toSpiceNode(tgt.path,'out1')) self.spiceText.append(plotLine) self.spiceText.append(".ENDC") self.spiceText.append(".END")
def addControlLines(self): """Add .CONTROL lines to spice netlist """ self.spiceText.append("\n\n** Simulation etc. ") self.spiceText.append("\n.CONTROL" "\nset hcopydevtype=postscript" "\nset hcopypscolor=true" "\nset color0 = white ;background" "\nset color1 = black ;text and grid" "\nset color2 = rgb:f/0/0 ;vector0" "\nset color3 = rgb:0/f/0 ;vector1" "\nset color3 = rgb:0/0/f ;vector2" "\nop") # Here we get the time of simulation and total steps done by moose. assert self.clock, "Main clock is not foind" simTime = self.clock.runTime dt = self.clock.dt self.spiceText.append("TRAN {0} {1}".format(dt, simTime)) plotLine = "HARDCOPY {}.ps ".format(self.outputFile) for t in self.tables: targets = t.neighbors['requestOut'] for tgt in targets: if tgt.className == 'PulseGen': debug.dump("INFO", "Stimulus pulse are not plotted by defualt.") continue plotLine += "V({}) ".format(self.toSpiceNode(tgt.path, 'out1')) self.spiceText.append(plotLine) self.spiceText.append(".ENDC") self.spiceText.append(".END")
def spiceLineForPulseGen(self, pulseGen): """Write spice-line for pulse """ pulsePath = idPathToObjPath(pulseGen.path) pulseName = self.moosePathToSpiceNode(pulsePath) td1 = pulseGen.delay[0] td2 = pulseGen.delay[1] width = pulseGen.width[0] level1 = 0.0 level2 = pulseGen.level[0] targets = pulseGen.neighbors['output'] if not targets: debug.dump("WARN", "It seems that pulse `%s` is not connected" % pulsePath) for i, t in enumerate(targets): spiceLine = "* Fist node is where current enters. A voltage \n" spiceLine += "* source is added in series. Useful for reading current\n" vtarget = self.toSpiceNode('%s%s' % (pulseName, i), 'x') target = self.toSpiceNode(t.path, 'inject') spiceLine += 'I{id}{name} GND {vtarget} dc 0 PULSE'.format( id=i, name=pulseName, vtarget=vtarget) spiceLine += '({level1} {level2} {TD} {TR} {TF} {PW} {PER})'.format( level1=level1, level2=level2, TD=td1, TR=0.0, TF=0.0, PW=width, PER=td1 + td2 + width) self.spiceText.append(spiceLine) # A a voltage source in series self.spiceText.append("V{id}{name} {vtarget} {target} dc 0".format( id=i, name=pulseName, vtarget=vtarget, target=target))
def plotTables(tables, outfile=None, **kwargs): """Plot a list of tables onto one figure only. """ assert type(tables) == dict, "Expected a dict of moose.Table" plt.figure(figsize=(10, 1.5*len(tables))) subplot = kwargs.get('subplot', True) for i, tname in enumerate(tables): if subplot: plt.subplot(len(tables), 1, i) yvec = tables[tname].vector xvec = np.linspace(0, moose.Clock('/clock').currentTime, len(yvec)) plt.plot(xvec, yvec, label=tname) plt.legend(loc='best', framealpha=0.4) plt.tight_layout() if outfile: pu.dump("PLOT", "Saving plots to file {}".format(outfile)) try: plt.savefig(outfile) except Exception as e: pu.dump("WARN" , "Failed to save figure, plotting onto a window" ) plt.show() else: plt.show()
def populateStoreHouse(self, **kwargs): """ Populate all data-structures related with Compartments, Tables, and pulse generators. """ debug.dump("INFO", "Populating data-structures to write spice netlist") self.getComparments(**kwargs) self.getTables(**kwargs) self.getPulseGens(**kwargs)
def toSpiceNode(self, moosePath, type): """Return spice node name for a given moose-path """ goodTypes = ["in1", "out1", "inject", 'x'] if type not in goodTypes: debug.dump("ERROR", "Bad node type: Expecting {}".format(goodTypes)) raise TypeError("Expecting {}, got {}".format(goodTypes, type)) moosePath = idPathToObjPath(moosePath) name = self.moosePathToSpiceNode(moosePath) return 'n{}{}'.format(name, type)
def test_disconnected_compartments(self): '''Test if any comparment is not connected ''' self.dump("Checking if any compartment is not connected ...") for c in self.mooseElems.compartments: if (c.neighbors['axial'] or c.neighbors['raxial']): continue elif c.neighbors['injectMsg']: continue else: msg = '%s is not connected with any other compartment' % c.path debug.dump('FAIL', [msg, 'Did you forget to use `moose.connect`?'])
def test_unused_tables(self): '''Tests if any table is not reading data. Such tables remain empty. ''' self.dump('Checking if any table is not connected') for table in self.mooseElems.tables: if table.neighbors['requestOut']: continue else: debug.dump('FAIL', [ 'Table {} is not reading data.'.format(table.path), ' Did you forget to use `moose.connect`?' ])
def toSpiceNode(self, moosePath, type): """Return spice node name for a given moose-path """ goodTypes = ["in1", "out1", "inject", 'x'] if type not in goodTypes: debug.dump("ERROR" , "Bad node type: Expecting {}".format(goodTypes) ) raise TypeError("Expecting {}, got {}".format(goodTypes, type)) moosePath = idPathToObjPath( moosePath ) name = self.moosePathToSpiceNode( moosePath ) return 'n{}{}'.format(name, type)
def writeGraphviz(filename=None, pat='/##[TYPE=Compartment]'): '''This is a generic function. It takes the the pattern, search for paths and write a graphviz file. ''' def fix(path): '''Fix a given path so it can be written to a graphviz file''' # If no [0] is at end of the path then append it. global pathPat if not pathPat.match(path): path = path + '[0]' return path pathList = getMoosePaths(pat) compList = _moose.wildcardFind(pat) if not compList: debug.dump("WARN" , "No compartment found" , frame = inspect.currentframe() ) return None dot = [] dot.append("digraph G {") dot.append("\tconcentrate=true;") for c in compList: if c.neighbors['raxial']: for n in c.neighbors['raxial']: lhs = fix(c.path) rhs = fix(n.path) dot.append('\t"{}" -> "{}";'.format(lhs, rhs)) elif c.neighbors['axial']: for n in c.neighbors['axial']: lhs = fix(c.path) rhs = fix(n.path) dot.append('\t"{}" -> "{}" [dir=back];'.format(lhs, rhs)) else: p = fix(c.path) dot.append('\t"{}"'.format(p)) dot.append('}') dot = '\n'.join(dot) if not filename: print(dot) else: with open(filename, 'w') as graphviz: debug.dump("INFO" , "Writing compartment topology to file {}".format(filename) ) graphviz.write(dot) return True
def writeGraphviz(filename=None, pat='/##[TYPE=Compartment]'): '''This is a generic function. It takes the the pattern, search for paths and write a graphviz file. ''' def fix(path): '''Fix a given path so it can be written to a graphviz file''' # If no [0] is at end of the path then append it. global pathPat if not pathPat.match(path): path = path + '[0]' return path pathList = getMoosePaths(pat) compList = _moose.wildcardFind(pat) if not compList: debug.dump("WARN" , "No compartment found" , frame = inspect.currentframe() ) dot = [] dot.append("digraph G {") dot.append("\tconcentrate=true;") for c in compList: if c.neighbors['raxial']: for n in c.neighbors['raxial']: lhs = fix(c.path) rhs = fix(n.path) dot.append('\t"{}" -> "{}";'.format(lhs, rhs)) elif c.neighbors['axial']: for n in c.neighbors['axial']: lhs = fix(c.path) rhs = fix(n.path) dot.append('\t"{}" -> "{}" [dir=back];'.format(lhs, rhs)) else: p = fix(c.path) dot.append('\t"{}"'.format(p)) dot.append('}') dot = '\n'.join(dot) if not filename: print(dot) else: with open(filename, 'w') as graphviz: debug.dump("INFO" , "Writing compartment topology to file {}".format(filename) ) graphviz.write(dot) return True
def test_isolated_pulse_gen(self): ''' Test if any pulse-generator is not injecting current to a compartment ''' self.dump('Checking if any pulse-generator is floating') for pg in self.mooseElems.pulseGens: if pg.neighbors['output']: continue else: debug.dump('FAIL', [ 'Current source {} is floating'.format(pg.path), 'It is not injecting current to any compartment', 'Perhaps you forgot to use `moose.connect`?' ])
def writeSpice(self, **kwargs): ''' Turn moose into spice ''' self.outputFile = kwargs.get('output', None) self.buildModel() spiceText = "\n".join(self.spiceText) if self.outputFile is not None: debug.dump("BACKEND", "Writing spice netlist to {}".format(self.outputFile)) with open(self.outputFile, "w") as spiceFile: spiceFile.write(spiceText) else: return spiceText
def test_unused_tables(self): '''Tests if any table is not reading data. Such tables remain empty. ''' self.dump('Checking if any table is not connected') for table in self.mooseElems.tables: if table.neighbors['requestOut']: continue else: debug.dump( 'FAIL' , [ 'Table {} is not reading data.'.format(table.path) , ' Did you forget to use `moose.connect`?' ] )
def test_disconnected_compartments(self): '''Test if any comparment is not connected ''' self.dump("Checking if any compartment is not connected ...") for c in self.mooseElems.compartments: if (c.neighbors['axial'] or c.neighbors['raxial']): continue elif c.neighbors['injectMsg']: continue else: msg = '%s is not connected with any other compartment' % c.path debug.dump('FAIL' , [ msg , 'Did you forget to use `moose.connect`?' ] )
def writeSpice(self, **kwargs): ''' Turn moose into spice ''' self.outputFile = kwargs.get('output', None) self.buildModel() spiceText = "\n".join( self.spiceText ) if self.outputFile is not None: debug.dump("BACKEND" , "Writing spice netlist to {}".format(self.outputFile) ) with open(self.outputFile, "w") as spiceFile: spiceFile.write( spiceText ) else: return spiceText
def test_clocks(self): """Tests if clocks are missing. """ self.dump("Checking if clocks are available") clock = self.mooseElems.clocks[0] clockDtList = clock.dts if np.count_nonzero(clockDtList) < 1: debug.dump("FATAL" , [ "No clock is found with non-zero dt size. " , "Did you forget to use `moose.setClock` function?" , "Quitting..." ] ) sys.exit(0) else: self.nonZeroClockIds = np.nonzero(self.mooseElems.clocks)
def plotTables(tables, file=None, **kwargs): """Plot a list of tables onto one figure only. """ assert type(tables) == list, "Expected a list of moose.Tables" for t in tables: plotTable(t, standalone=False, file=None, **kwargs) if file: pu.dump("PLOT", "Saving plots to file {}".format(file)) try: plt.savefig(file) except Exception as e: pu.dump("WARN", "Failed to save figure, plotting onto a window") plt.show() else: plt.show()
def recordTarget(tablePath, target, field = 'vm', **kwargs): """Setup a table to record at given path. Make sure that all root paths in tablePath exists. Returns a table. """ # If target is not an moose object but a string representing intended path # then we need to fetch the object first. if type( target) == str: if not _moose.exists(target): msg = "Given target `{}` does not exists. ".format( target ) raise RuntimeError( msg ) else: target = _moose.Neutral( target ) assert target.path, "Target must have a valid moose path." table = _moose.Table( tablePath ) assert table # Sanities field. if field == "output": pass elif 'get' not in field: field = 'get'+field[0].upper()+field[1:] else: field = field[:2]+field[3].upper()+field[4:] try: print_utils.dump("TABLE" , "Connecting table {} to target {} field {}".format( table.path , target.path , field ) ) table.connect( 'requestOut', target, field ) except Exception as e: debug.dump("ERROR" , [ "Failed to connect table to target" , e ] ) raise e assert table, "Moose is not able to create a recording table" return table
def test_isolated_pulse_gen(self): ''' Test if any pulse-generator is not injecting current to a compartment ''' self.dump('Checking if any pulse-generator is floating') for pg in self.mooseElems.pulseGens: if pg.neighbors['output']: continue else: debug.dump( 'FAIL' , [ 'Current source {} is floating'.format(pg.path) , 'It is not injecting current to any compartment' , 'Perhaps you forgot to use `moose.connect`?' ] )
def plotTables(tables, file=None, **kwargs): """Plot a list of tables onto one figure only. """ assert type(tables) == list, "Expected a list of moose.Tables" for t in tables: plotTable(t, standalone = False, file = None, **kwargs) if file: debug.dump("PLOT", "Saving plots to file {}".format(file)) try: pylab.savefig(file) except Exception as e: debug.dump("WARN" , "Failed to save figure, plotting onto a window" ) pylab.show() else: pylab.show()
def spiceLineForPulseGen(self, pulseGen): """Write spice-line for pulse """ pulsePath = idPathToObjPath( pulseGen.path ) pulseName = self.moosePathToSpiceNode( pulsePath ) td1 = pulseGen.delay[0] td2 = pulseGen.delay[1] width = pulseGen.width[0] level1 = 0.0 level2 = pulseGen.level[0] targets = pulseGen.neighbors['output'] if not targets: debug.dump("WARN" , "It seems that pulse `%s` is not connected" % pulsePath ) for i, t in enumerate(targets): spiceLine = "* Fist node is where current enters. A voltage \n" spiceLine += "* source is added in series. Useful for reading current\n" vtarget = self.toSpiceNode('%s%s'%(pulseName, i), 'x') target = self.toSpiceNode( t.path, 'inject' ) spiceLine += 'I{id}{name} GND {vtarget} dc 0 PULSE'.format( id = i , name = pulseName , vtarget = vtarget ) spiceLine += '({level1} {level2} {TD} {TR} {TF} {PW} {PER})'.format( level1 = level1 , level2 = level2 , TD = td1 , TR = 0.0 , TF = 0.0 , PW = width , PER = td1 + td2 + width ) self.spiceText.append(spiceLine) # A a voltage source in series self.spiceText.append( "V{id}{name} {vtarget} {target} dc 0".format( id = i , name = pulseName , vtarget = vtarget , target = target ) )
def test_synapse(self, synapses): if type(synapses) == moose.Synapse: synapse = synapses elif type(synapses) == moose.vec: if len(synapses) == 1: synapse = synapses[0] else: [self.test_synapse(x) for x in synapses] spikeGens = synapse.neighbors['addSpike'] if not spikeGens: debug.dump('FAIL', [ " Synapse %s has no incoming spikes" % synapse.path, " Did you forget to connect a moose.SpikeGen e.g." " moose.connect(spikegen, 'spikeOut', synapse, 'addSpike')" ]) else: [self.test_spikegen(x) for x in spikeGens]
def test_clocks(self): """Tests if clocks are missing. """ self.dump("Checking if clocks are available") try: clock = self.mooseElems.clocks[0] except: debug.dump("WARN", "Could not find any clock") return clockDtList = clock.dts if np.count_nonzero(clockDtList) < 1: debug.dump("FATAL", [ "No clock is found with non-zero dt size. ", "Did you forget to use `moose.setClock` function?", "Quitting..." ]) sys.exit(0) else: self.nonZeroClockIds = np.nonzero(self.mooseElems.clocks)
def test_spikegen(self, spikegens): spikeGen = None if len(spikegens) > 1: [self.test_spikegen(x) for x in spikegens] elif len(spikegens) == 1: spikeGen = spikegens[0] elif type(spikegens) == moose.SpikeGen: spikeGen = spikegens pre = spikeGen.neighbors['Vm'] if not pre: debug.dump('FAIL', [ "SpikeGen %s is not reading Vm of any compartment " % spikeGen.path, "Did you forget to connect Vm of a " "compartment to this SpikeGen? " " e.g. moose.connect(comp, 'VmOut', spikeGen, 'Vm')" ]) else: pass
def test_synapses(self): self.dump("Checking if any synapse is dead") for synchan in self.mooseElems.synchans: if not synchan.neighbors['channel']: debug.dump("FAIL" , [ "SynChan %s is not receiving any input " % synchan.path , " No incoming 'channel'. " " Did you forget to connect compartment e.g." "moose.connect(synchan, 'channel', comp, 'channel')" " where synchan is 'moose.SynChan' and comp is " " 'moose.Compartment'" ] ) sys.exit() else: for synapse in synchan.synapses: debug.dump("TODO" , "Write verification test for output of each synapse" , frame = inspect.curretframe() )
def checkSentitivity(self, methodName, objectList): """Check if a given method is sensitive to any non-zero clock """ assert type(methodName) == str insensitiveObjectList = [] for obj in objectList: if not obj.neighbors[methodName]: insensitiveObjectList.append(obj) else: # Here we must check if method is made sensitive to a # zero-clock. Currently there is no way to test it in python. pass if len(insensitiveObjectList) > 0: msgList = [ "Method `%s` is insensitive to all clocks. " % methodName, "Total {} out of {} object ({}) fails this test".format( len(insensitiveObjectList), len(objectList), type(insensitiveObjectList[0])) ] debug.dump("FAIL", msgList)
def test_synchans(self): self.dump("Checking if any synapse is dead") for synchan in self.mooseElems.synchans: if synchan.Gbar <= 0.0: debug.dump("WARN", [ synchan.path, "Gbar value is zero or negative: %s" % synchan.Gbar, "Not cool!" ]) # Check the output of synchan. if not synchan.neighbors['channel']: debug.dump("FAIL", [ "SynChan %s is not connected to post-compartment" % synchan.path, " No connected 'channel'. " " Did you forget to connect compartment e.g." "moose.connect(synchan, 'channel', comp, 'channel')" " where synchan is 'moose.SynChan' and comp is " " 'moose.Compartment'?" ]) else: pass # Check if anyone is activating this synchan. synhandlers = synchan.neighbors['activation'] if not synhandlers: debug.dump("FAIL", [ "No SynHandler is activating SynChan %s" % synchan.path, " Did you forget to connect a SynHandler e.g. " "moose.connect(synHandler, 'activationOut', synchan, 'activation'" " where synchan is 'moose.SynChan' and synHandler is" " moose.SynHandler." ]) else: [self.test_synhandler(x) for x in synhandlers]
def plotTable(table, standalone=True, file=None, **kwargs): """Plot a given table. It plots table.vector This function can scale the x-axis. By default, y-axis and x-axis scaling is done by a factor of 1. Pass 'xscale' and/or 'yscale' argument to function to modify scales. """ if not type(table) == _moose.Table: msg = "Expected moose.Table, got {}".format( type(table) ) raise TypeError(msg) if standalone: pylab.figure() vecX, vecY = reformatTable(table, kwargs) pylab.plot(vecX, vecY) if file and standalone: debug.dump("PLOT", "Saving plot to {}".format(file)) pylab.savefig(file) elif standalone: pylab.show()
def plotTable(table, standalone=True, file=None, **kwargs): """Plot a given table. It plots table.vector This function can scale the x-axis. By default, y-axis and x-axis scaling is done by a factor of 1. Pass 'xscale' and/or 'yscale' argument to function to modify scales. """ if not type(table) == moose.Table: msg = "Expected moose.Table, got {}".format(type(table)) raise TypeError(msg) if standalone: plt.figure() vecX, vecY = reformatTable(table, kwargs) plt.plot(vecX, vecY) if file and standalone: pu.dump("PLOT", "Saving plot to {}".format(file)) plt.savefig(file) elif standalone: plt.show()
def checkSentitivity( self, methodName, objectList): """Check if a given method is sensitive to any non-zero clock """ assert type(methodName) == str insensitiveObjectList = [] for obj in objectList: if not obj.neighbors[methodName]: insensitiveObjectList.append(obj) else: # Here we must check if method is made sensitive to a # zero-clock. Currently there is no way to test it in python. pass if len(insensitiveObjectList) > 0: msgList = [ "Method `%s` is insensitive to all clocks. " % methodName , "Total {} out of {} object ({}) fails this test".format( len(insensitiveObjectList) , len(objectList) , type(insensitiveObjectList[0]) ) ] debug.dump("FAIL", msgList)
def saveTables(tables, file=None, **kwargs): """Save a list to tables to a data file. """ assert type(tables) == list, "Expecting a list of moose.Table" plots = [] xaxis = None for t in tables: vecX, vecY = reformatTable(t, kwargs) plots.append(vecY) if xaxis: if xaxis != vecX: raise UserWarning("Tables must have same x-axis") else: xaxis = vecX tableText = "" for i, x in enumerate(xaxis): tableText += "{} ".format(x) tableText += " ".join(['%s' % p[i] for p in plots]) tableText += "\n" if file is None: print(tableText) else: pu.dump("PLOT", "Saving tables data to file {}".format(file)) with open(file, "w") as f: f.write(tableText)
def saveTables(tables, file=None, **kwargs): """Save a list to tables to a data file. """ assert type(tables) == list, "Expecting a list of moose.Table" plots = [] xaxis = None for t in tables: vecX, vecY = reformatTable(t, kwargs) plots.append(vecY) if xaxis: if xaxis != vecX: raise UserWarning("Tables must have same x-axis") else: xaxis = vecX tableText = "" for i, x in enumerate(xaxis): tableText += "{} ".format(x) tableText += " ".join(['%s'%p[i] for p in plots]) tableText += "\n" if file is None: print(tableText) else: debug.dump("PLOT", "Saving tables data to file {}".format(file)) with open(file, "w") as f: f.write(tableText)