def add_input_source(self, input_source, network): input_params = input_source.parameters if input_source.parameters else {} for ip in input_params: input_params[ip] = evaluate(input_params[ip], network.parameters) ''' This is a quick hack to support noisyCurrentSource before that type is integrated into the core of NeuroML... TODO: remove when integrated! ''' if input_source.lems_source_file and 'noisyCurrentSource' in input_source.id: pynn_input_params = {} for p in input_params: if p=='delay': pynn_input_params['start'] = convert_to_units(input_params[p],'ms') elif p=='duration': pynn_input_params['stop'] = convert_to_units(input_params[p],'ms') + convert_to_units(input_params['delay'],'ms') elif p=='mean': pynn_input_params['mean'] = convert_to_units(input_params[p],'nA') elif p=='stdev': pynn_input_params['stdev'] = convert_to_units(input_params[p],'nA') elif p=='noiseDt': pynn_input_params['dt'] = convert_to_units(input_params[p],'ms') else: raise Exception('Parameter %s=%s is not appropriate for inout %s'%(p,input_params[p],input_source.id)) exec('self.input_sources["%s"] = self.sim.NoisyCurrentSource(**pynn_input_params)'%(input_source.id)) else: exec('self.input_sources["%s"] = self.sim.%s(**input_params)'%(input_source.id,input_source.pynn_input))
def test_conversion(nml2_quantity, unit, expected, should_fail=False): try: val = pynml.convert_to_units(nml2_quantity, unit, True) print(' Converted %s -> %s %s (expecting: %s)' % (nml2_quantity, val, unit, expected)) assert abs(val / expected - 1) < 1e-12 except Exception as e: assert should_fail print("Correctly caught exception: %s" % e)
def test_conversion(nml2_quantity, unit, expected, should_fail=False): try: val = pynml.convert_to_units(nml2_quantity, unit, True) print(' Converted %s -> %s %s (expecting: %s)'%(nml2_quantity, val, unit,expected)) assert abs(val/expected - 1) < 1e-12 except Exception as e: assert should_fail print("Correctly caught exception: %s"%e)
def get_reversal_potential_mV(self, synapse_obj): if hasattr(synapse_obj, 'erev'): return convert_to_units(synapse_obj.erev, 'mV') elif hasattr(synapse_obj, 'e_rev'): # PyNN, no units, in mV return float(synapse_obj.e_rev) else: return None
def _get_gbase_nS(self, projName, return_orig_string_also=False): gbase_nS = None gbase = '???' #print('Getting gbase for %s'%projName) if projName in self.proj_syn_objs: syn = self.proj_syn_objs[projName] if hasattr(syn,'gbase'): gbase = syn.gbase gbase_nS = convert_to_units(gbase, 'nS') elif hasattr(syn,'conductance'): gbase = syn.conductance gbase_nS = convert_to_units(gbase, 'nS') self.syn_conds_used[syn.id] = '%s nS (%s)'%(gbase_nS, gbase) else: self.syn_conds_used[projName] = 'Syn for %s not found...'%projName if return_orig_string_also: return gbase_nS, gbase return gbase_nS
def _get_gbase_nS(self, projName, return_orig_string_also=False): gbase_nS = None gbase = "???" # print('Getting gbase for %s'%projName) if projName in self.proj_syn_objs: syn = self.proj_syn_objs[projName] if hasattr(syn, "gbase"): gbase = syn.gbase gbase_nS = convert_to_units(gbase, "nS") elif hasattr(syn, "conductance"): gbase = syn.conductance gbase_nS = convert_to_units(gbase, "nS") self.syn_conds_used[syn.id] = "%s nS (%s)" % (gbase_nS, gbase) else: self.syn_conds_used[projName] = "Syn for %s not found..." % projName if return_orig_string_also: return gbase_nS, gbase return gbase_nS
def add_input_source(self, input_source, network): input_params = input_source.parameters if input_source.parameters else {} for ip in input_params: input_params[ip] = evaluate(input_params[ip], network.parameters) """ This is a quick hack to support noisyCurrentSource before that type is integrated into the core of NeuroML... TODO: remove when integrated! """ if input_source.lems_source_file and "noisyCurrentSource" in input_source.id: pynn_input_params = {} for p in input_params: if p == "delay": pynn_input_params["start"] = convert_to_units( input_params[p], "ms") elif p == "duration": pynn_input_params["stop"] = convert_to_units( input_params[p], "ms") + convert_to_units( input_params["delay"], "ms") elif p == "mean": pynn_input_params["mean"] = convert_to_units( input_params[p], "nA") elif p == "stdev": pynn_input_params["stdev"] = convert_to_units( input_params[p], "nA") elif p == "noiseDt": pynn_input_params["dt"] = convert_to_units( input_params[p], "ms") else: raise Exception( "Parameter %s=%s is not appropriate for inout %s" % (p, input_params[p], input_source.id)) exec( 'self.input_sources["%s"] = self.sim.NoisyCurrentSource(**pynn_input_params)' % (input_source.id)) else: exec('self.input_sources["%s"] = self.sim.%s(**input_params)' % (input_source.id, input_source.pynn_input))
def finalise_input_source(self, inputListId): if self.include_inputs: #self.print_input_information('FINAL: '+inputListId, self.pops_ils[inputListId], '...', self.sizes_ils[inputListId]) if self.level >= 2: label = '<%s' % inputListId if self.level >= 3: size = self.sizes_ils[inputListId] label += '<br/><i>%s input%s</i>' % (size, '' if size == 1 else 's') if self.level >= 4: from neuroml import PulseGenerator from neuroml import TransientPoissonFiringSynapse from neuroml import PoissonFiringSynapse from pyneuroml.pynml import convert_to_units input_comp_obj = self.input_comp_obj_ils[inputListId] if input_comp_obj and isinstance(input_comp_obj, PulseGenerator): start = convert_to_units(input_comp_obj.delay, 'ms') if start == int(start): start = int(start) duration = convert_to_units(input_comp_obj.duration, 'ms') if duration == int(duration): duration = int(duration) amplitude = convert_to_units(input_comp_obj.amplitude, 'pA') if amplitude == int(amplitude): amplitude = int(amplitude) label += '<br/>Pulse %s-%sms@%spA' % ( start, start + duration, amplitude) if input_comp_obj and isinstance(input_comp_obj, PoissonFiringSynapse): average_rate = convert_to_units( input_comp_obj.average_rate, 'Hz') if average_rate == int(average_rate): average_rate = int(average_rate) label += '<br/>Syn: %s @ %sHz' % ( input_comp_obj.synapse, average_rate) if input_comp_obj and isinstance( input_comp_obj, TransientPoissonFiringSynapse): start = convert_to_units(input_comp_obj.delay, 'ms') if start == int(start): start = int(start) duration = convert_to_units(input_comp_obj.duration, 'ms') if duration == int(duration): duration = int(duration) average_rate = convert_to_units( input_comp_obj.average_rate, 'Hz') if average_rate == int(average_rate): average_rate = int(average_rate) label += '<br/>Syn: %s<br/>%s-%sms @ %sHz' % ( input_comp_obj.synapse, start, start + duration, average_rate) label += '>' self.f.attr('node', color='#444444', style='', fontcolor='#444444') self.f.node(inputListId, label=label) label = None if self.level >= 5: label = '<' if self.sizes_ils[inputListId] > 0: percent = 100 * float( self.sizes_ils[inputListId]) / self.pop_sizes[ self.pops_ils[inputListId]] if percent <= 100: label += '%s%s%% of population<br/> ' % ( ' ' if percent != 100 else '', self.format_float(percent)) else: label += '%s%s per cell<br/> ' % ( ' ', self.format_float(percent / 100)) avg_weight = float(self.weights_ils[inputListId] ) / self.sizes_ils[inputListId] label += 'avg weight: %s<br/> ' % (self.format_float( avg_weight, approx=True)) if not label[-1] == '>': label += '>' self.f.edge(inputListId, self.pops_ils[inputListId], arrowhead=self.INPUT_ARROW_SHAPE, label=label)
def handle_population(self, population_id, component, size=-1, component_obj=None, properties={}, notes=None): sizeInfo = " as yet unspecified size" if size >= 0: sizeInfo = ", size: " + str(size) + " cells" if component_obj: compInfo = " (%s)" % component_obj.__class__.__name__ else: compInfo = "" self.pop_sizes[population_id] = size print_v("Population: " + population_id + ", component: " + component + compInfo + sizeInfo + ", properties: %s" % properties) color = '#444444' fcolor = '#ffffff' shape = self.DEFAULT_POP_SHAPE if properties and 'color' in properties: rgb = properties['color'].split() color = '#' for a in rgb: color = color + '%02x' % int(float(a) * 255) # https://stackoverflow.com/questions/3942878 if (float(rgb[0]) * 0.299 + float(rgb[1]) * 0.587 + float(rgb[2]) * 0.2) > .25: fcolor = '#000000' else: fcolor = '#ffffff' #print('Color %s -> %s -> %s'%(properties['color'], rgb, color)) if properties and 'type' in properties: self.pop_types[population_id] = properties['type'] if properties['type'] == 'E': shape = self.EXC_POP_SHAPE if properties['type'] == 'I': shape = self.INH_POP_SHAPE self.pop_colors[population_id] = color label = '<%s' % population_id if self.level >= 3: label += '<br/><i>%s cell%s</i>' % (size, '' if size == 1 else 's') if self.level >= 4: from neuroml import SpikeSourcePoisson if component_obj and isinstance(component_obj, SpikeSourcePoisson): start = convert_to_units(component_obj.start, 'ms') if start == int(start): start = int(start) duration = convert_to_units(component_obj.duration, 'ms') if duration == int(duration): duration = int(duration) rate = convert_to_units(component_obj.rate, 'Hz') if rate == int(rate): rate = int(rate) label += '<br/>Spikes %s-%sms@%sHz' % (start, start + duration, rate) else: label += '<br/>%s' % (component) label += '>' if properties and 'region' in properties: with self.f.subgraph(name='cluster_%s' % properties['region']) as c: c.attr(color='#444444', fontcolor='#444444') c.attr(label=properties['region']) c.attr('node', color=color, style='filled', fontcolor=fcolor, shape=shape) if self.is_cell_level(): for i in range(size): cell_info = self.get_cell_identifier(population_id, i) c.node(cell_info, label=cell_info) else: c.node(population_id, label=label) else: self.f.attr('node', color=color, style='filled', fontcolor=fcolor, shape=shape) if self.is_cell_level(): for i in range(size): cell_info = self.get_cell_identifier(population_id, i) self.f.node(cell_info, label=cell_info) else: self.f.node(population_id, label=label)
def setCellRuleDynamicParamsFromNeuroml_old(self, cell, cellRule): segGroupKeys = set([sec.split('_')[0] for sec in cellRule['secs']]) seg_grps_vs_nrn_sections = {segGroup: [sec for sec in cellRule['secs'] if sec.startswith(segGroup)] for segGroup in segGroupKeys} seg_grps_vs_nrn_sections['all'] = list(cellRule['secs']) inhomogeneous_parameters = {segGroup: [] for segGroup in segGroupKeys} # how to fill in this from swc file? for cm in cell.biophysical_properties.membrane_properties.channel_densities: group = 'all' if not cm.segment_groups else cm.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: gmax = pynml.convert_to_units(cm.cond_density,'S_per_cm2') if cm.ion_channel=='pas': mech = {'g':gmax} else: mech = {'gbar':gmax} erev = pynml.convert_to_units(cm.erev,'mV') cellRule['secs'][section_name]['mechs'][cm.ion_channel] = mech ion = self._determine_ion(cm) if ion == 'non_specific': mech['e'] = erev else: if 'ions' not in cellRule['secs'][section_name]: cellRule['secs'][section_name]['ions'] = {} if ion not in cellRule['secs'][section_name]['ions']: cellRule['secs'][section_name]['ions'][ion] = {} cellRule['secs'][section_name]['ions'][ion]['e'] = erev for cm in cell.biophysical_properties.membrane_properties.channel_density_v_shifts: group = 'all' if not cm.segment_groups else cm.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: gmax = pynml.convert_to_units(cm.cond_density,'S_per_cm2') if cm.ion_channel=='pas': mech = {'g':gmax} else: mech = {'gbar':gmax} erev = pynml.convert_to_units(cm.erev,'mV') cellRule['secs'][section_name]['mechs'][cm.ion_channel] = mech ion = self._determine_ion(cm) if ion == 'non_specific': mech['e'] = erev else: if 'ions' not in cellRule['secs'][section_name]: cellRule['secs'][section_name]['ions'] = {} if ion not in cellRule['secs'][section_name]['ions']: cellRule['secs'][section_name]['ions'][ion] = {} cellRule['secs'][section_name]['ions'][ion]['e'] = erev mech['vShift'] = pynml.convert_to_units(cm.v_shift,'mV') for cm in cell.biophysical_properties.membrane_properties.channel_density_nernsts: group = 'all' if not cm.segment_groups else cm.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: gmax = pynml.convert_to_units(cm.cond_density,'S_per_cm2') if cm.ion_channel=='pas': mech = {'g':gmax} else: mech = {'gbar':gmax} cellRule['secs'][section_name]['mechs'][cm.ion_channel] = mech #TODO: erev!! ion = self._determine_ion(cm) if ion == 'non_specific': pass ##mech['e'] = erev else: if 'ions' not in cellRule['secs'][section_name]: cellRule['secs'][section_name]['ions'] = {} if ion not in cellRule['secs'][section_name]['ions']: cellRule['secs'][section_name]['ions'][ion] = {} ##cellRule['secs'][section_name]['ions'][ion]['e'] = erev for cm in cell.biophysical_properties.membrane_properties.channel_density_ghk2s: group = 'all' if not cm.segment_groups else cm.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: gmax = pynml.convert_to_units(cm.cond_density,'S_per_cm2') if cm.ion_channel=='pas': mech = {'g':gmax} else: mech = {'gbar':gmax} ##erev = pynml.convert_to_units(cm.erev,'mV') cellRule['secs'][section_name]['mechs'][cm.ion_channel] = mech ion = self._determine_ion(cm) if ion == 'non_specific': pass #mech['e'] = erev else: if 'ions' not in cellRule['secs'][section_name]: cellRule['secs'][section_name]['ions'] = {} if ion not in cellRule['secs'][section_name]['ions']: cellRule['secs'][section_name]['ions'][ion] = {} ##cellRule['secs'][section_name]['ions'][ion]['e'] = erev for cm in cell.biophysical_properties.membrane_properties.channel_density_non_uniforms: for vp in cm.variable_parameters: if vp.parameter=="condDensity": iv = vp.inhomogeneous_value grp = vp.segment_groups path_vals = inhomogeneous_parameters[grp] expr = iv.value.replace('exp(','math.exp(') #print("variable_parameter: %s, %s, %s"%(grp,iv, expr)) for section_name in seg_grps_vs_nrn_sections[grp]: path_start, path_end = inhomogeneous_parameters[grp][section_name] p = path_start gmax_start = pynml.convert_to_units('%s S_per_m2'%eval(expr),'S_per_cm2') p = path_end gmax_end = pynml.convert_to_units('%s S_per_m2'%eval(expr),'S_per_cm2') nseg = cellRule['secs'][section_name]['geom']['nseg'] if 'nseg' in cellRule['secs'][section_name]['geom'] else 1 #print(" Cond dens %s: %s S_per_cm2 (%s um) -> %s S_per_cm2 (%s um); nseg = %s"%(section_name,gmax_start,path_start,gmax_end,path_end, nseg)) gmax = [] for fract in [(2*i+1.0)/(2*nseg) for i in range(nseg)]: p = path_start + fract*(path_end-path_start) gmax_i = pynml.convert_to_units('%s S_per_m2'%eval(expr),'S_per_cm2') #print(" Point %s at %s = %s"%(p,fract, gmax_i)) gmax.append(gmax_i) if cm.ion_channel=='pas': mech = {'g':gmax} else: mech = {'gbar':gmax} erev = pynml.convert_to_units(cm.erev,'mV') cellRule['secs'][section_name]['mechs'][cm.ion_channel] = mech ion = self._determine_ion(cm) if ion == 'non_specific': mech['e'] = erev else: if 'ions' not in cellRule['secs'][section_name]: cellRule['secs'][section_name]['ions'] = {} if ion not in cellRule['secs'][section_name]['ions']: cellRule['secs'][section_name]['ions'][ion] = {} cellRule['secs'][section_name]['ions'][ion]['e'] = erev for cm in cell.biophysical_properties.membrane_properties.channel_density_ghks: raise Exception("<channelDensityGHK> not yet supported!") for cm in cell.biophysical_properties.membrane_properties.channel_density_non_uniform_nernsts: raise Exception("<channelDensityNonUniformNernst> not yet supported!") for cm in cell.biophysical_properties.membrane_properties.channel_density_non_uniform_ghks: raise Exception("<channelDensityNonUniformGHK> not yet supported!") for vi in cell.biophysical_properties.membrane_properties.init_memb_potentials: group = 'all' if not vi.segment_groups else vi.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: cellRule['secs'][section_name]['vinit'] = pynml.convert_to_units(vi.value,'mV') # remove default vinit if vi empty so the global h.v_init is used if len(cell.biophysical_properties.membrane_properties.init_memb_potentials) == 0: group = 'all' for section_name in seg_grps_vs_nrn_sections[group]: del cellRule['secs'][section_name]['vinit'] for sc in cell.biophysical_properties.membrane_properties.specific_capacitances: group = 'all' if not sc.segment_groups else sc.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: cellRule['secs'][section_name]['geom']['cm'] = pynml.convert_to_units(sc.value,'uF_per_cm2') if hasattr(cell.biophysical_properties.intracellular_properties, 'resistivities'): for ra in cell.biophysical_properties.intracellular_properties.resistivities: group = 'all' if not ra.segment_groups else ra.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: cellRule['secs'][section_name]['geom']['Ra'] = pynml.convert_to_units(ra.value,'ohm_cm') concentrationModelParams = {} excludeConcentrationModel = ['id', 'type', 'ion'] if hasattr(cell, 'concentrationModel'): concentrationModelParams[cell.concentratrionModel.id] = {} for param in cell.concentratrionModel: if param not in excludeConcentrationModel: concentrationModelParams[cell.concentratrionModel.id][param] = getattr(cell.concentratrionModel, param) if hasattr(cell.biophysical_properties.intracellular_properties, 'species'): for specie in cell.biophysical_properties.intracellular_properties.species: group = 'all' if not specie.segment_groups else specie.segment_groups for section_name in seg_grps_vs_nrn_sections[group]: cellRule['secs'][section_name]['ions'][specie.ion]['o'] = pynml.convert_to_units(specie.initial_ext_concentration,'mM') cellRule['secs'][section_name]['ions'][specie.ion]['i'] = pynml.convert_to_units(specie.initial_concentration,'mM') #cellRule['secs'][section_name]['mechs'][cell.concentratrionModel] = concentrationModelParams #print(cell.concentratrionModel) print(concentrationModelParams) return cellRule
def finalise_input_source(self, inputListId): if self.include_ext_inputs: # self.print_input_information('FINAL: '+inputListId, self.pops_ils[inputListId], '...', self.sizes_ils[inputListId]) if self.level >= 2: label = "<%s" % inputListId if self.level >= 3: size = self.sizes_ils[inputListId] label += "<br/><i>%s input%s</i>" % (size, "" if size == 1 else "s") if self.level >= 4: from neuroml import PulseGenerator from neuroml import TransientPoissonFiringSynapse from neuroml import PoissonFiringSynapse from pyneuroml.pynml import convert_to_units input_comp_obj = self.input_comp_obj_ils[inputListId] if input_comp_obj and isinstance(input_comp_obj, PulseGenerator): start = convert_to_units(input_comp_obj.delay, "ms") if start == int(start): start = int(start) duration = convert_to_units(input_comp_obj.duration, "ms") if duration == int(duration): duration = int(duration) amplitude = convert_to_units(input_comp_obj.amplitude, "pA") if amplitude == int(amplitude): amplitude = int(amplitude) label += "<br/>Pulse %s-%sms@%spA" % ( start, start + duration, amplitude, ) if input_comp_obj and isinstance(input_comp_obj, PoissonFiringSynapse): average_rate = convert_to_units( input_comp_obj.average_rate, "Hz") if average_rate == int(average_rate): average_rate = int(average_rate) label += "<br/>Syn: %s @ %sHz" % ( input_comp_obj.synapse, average_rate, ) if input_comp_obj and isinstance( input_comp_obj, TransientPoissonFiringSynapse): start = convert_to_units(input_comp_obj.delay, "ms") if start == int(start): start = int(start) duration = convert_to_units(input_comp_obj.duration, "ms") if duration == int(duration): duration = int(duration) average_rate = convert_to_units( input_comp_obj.average_rate, "Hz") if average_rate == int(average_rate): average_rate = int(average_rate) label += "<br/>Syn: %s<br/>%s-%sms @ %sHz" % ( input_comp_obj.synapse, start, start + duration, average_rate, ) label += ">" self.graph.attr("node", color="#444444", style="", fontcolor="#444444") self.graph.node(inputListId, label=label) label = None if self.level >= 5: label = "<" if self.sizes_ils[inputListId] > 0: percent = (100 * float(self.sizes_ils[inputListId]) / self.pop_sizes[self.pops_ils[inputListId]]) if percent <= 100: label += "%s%s%% of population<br/> " % ( " " if percent != 100 else "", self.format_float(percent), ) else: label += "%s%s per cell<br/> " % ( " ", self.format_float(percent / 100), ) avg_weight = (float(self.weights_ils[inputListId]) / self.sizes_ils[inputListId]) label += "avg weight: %s<br/> " % (self.format_float( avg_weight, approx=True)) if not label[-1] == ">": label += ">" self.graph.edge( inputListId, self.pops_ils[inputListId], arrowhead=self.INPUT_ARROW_SHAPE, label=label, )
def handle_population( self, population_id, component, size=-1, component_obj=None, properties={}, notes=None, ): sizeInfo = " as yet unspecified size" if size >= 0: sizeInfo = ", size: " + str(size) + " cells" if component_obj: compInfo = " (%s)" % component_obj.__class__.__name__ else: compInfo = "" self.pop_nml_component_objs[population_id] = component_obj if not self.include_input_pops and is_spiking_input_nml_cell( component_obj): print("Ignoring %s as it's a spiking input population") return self.pop_sizes[population_id] = size print_v("Population: " + population_id + ", component: " + component + compInfo + sizeInfo + ", properties: %s" % properties) color = "#444444" fcolor = "#ffffff" shape = self.DEFAULT_POP_SHAPE if properties and "color" in properties: rgb = properties["color"].split() color = "#" for a in rgb: color = color + "%02x" % int(float(a) * 255) # https://stackoverflow.com/questions/3942878 if (float(rgb[0]) * 0.299 + float(rgb[1]) * 0.587 + float(rgb[2]) * 0.2) > 0.25: fcolor = "#000000" else: fcolor = "#ffffff" # print('Color %s -> %s -> %s'%(properties['color'], rgb, color)) if properties and "type" in properties: self.pop_types[population_id] = properties["type"] if properties["type"] == "E": shape = self.EXC_POP_SHAPE if properties["type"] == "I": shape = self.INH_POP_SHAPE self.pop_colors[population_id] = color label = "<%s" % population_id if self.level >= 3: label += "<br/><i>%s cell%s</i>" % (size, "" if size == 1 else "s") if self.level >= 4: from neuroml import SpikeSourcePoisson if component_obj and isinstance(component_obj, SpikeSourcePoisson): start = convert_to_units(component_obj.start, "ms") if start == int(start): start = int(start) duration = convert_to_units(component_obj.duration, "ms") if duration == int(duration): duration = int(duration) rate = convert_to_units(component_obj.rate, "Hz") if rate == int(rate): rate = int(rate) label += "<br/>Spikes %s-%sms@%sHz" % (start, start + duration, rate) else: label += "<br/>%s" % (component) label += ">" if properties and "region" in properties: with self.graph.subgraph(name="cluster_%s" % properties["region"]) as c: c.attr(color="#444444", fontcolor="#444444") c.attr(label=properties["region"]) c.attr("node", color=color, style="filled", fontcolor=fcolor, shape=shape) if self.is_cell_level(): for i in range(size): cell_info = self.get_cell_identifier(population_id, i) c.node(cell_info, label=cell_info) else: c.node(population_id, label=label) else: self.graph.attr("node", color=color, style="filled", fontcolor=fcolor, shape=shape) if self.is_cell_level(): for i in range(size): cell_info = self.get_cell_identifier(population_id, i) self.graph.node(cell_info, label=cell_info) else: self.graph.node(population_id, label=label)