def create_cell(cell_id): # type: (str) -> Cell """Create a NeuroML Cell. Initialises the cell with these properties assigning IDs where applicable: - Morphology: "morphology" - BiophysicalProperties: "biophys" - MembraneProperties - IntracellularProperties - SegmentGroups: "all", "soma_group", "dendrite_group", "axon_group" which can be used to include all, soma, dendrite, and axon segments respectively. Note that since this cell does not currently include a segment in its morphology, it is *not* a valid NeuroML construct. Use the `add_segment` function to add segments. `add_segment` will also populate the default segment groups this creates. :param cell_id: id of the cell :type cell_id: str :returns: created cell object of type neuroml.Cell """ cell = Cell(id=cell_id) cell.morphology = Morphology(id='morphology') membrane_properties = MembraneProperties() intracellular_properties = IntracellularProperties() cell.biophysical_properties = BiophysicalProperties( id="biophys", intracellular_properties=intracellular_properties, membrane_properties=membrane_properties) seg_group_all = SegmentGroup(id='all') seg_group_soma = SegmentGroup( id='soma_group', neuro_lex_id=neuro_lex_ids["soma"], notes="Default soma segment group for the cell") seg_group_axon = SegmentGroup( id='axon_group', neuro_lex_id=neuro_lex_ids["axon"], notes="Default axon segment group for the cell") seg_group_dend = SegmentGroup( id='dendrite_group', neuro_lex_id=neuro_lex_ids["dend"], notes="Default dendrite segment group for the cell") cell.morphology.segment_groups.append(seg_group_all) cell.morphology.segment_groups.append(seg_group_soma) cell.morphology.segment_groups.append(seg_group_axon) cell.morphology.segment_groups.append(seg_group_dend) return cell
def run(): cell_num = 2 nml_doc = NeuroMLDocument(id="demo_attraction") net = Network(id="demo_attraction") nml_doc.networks.append(net) for cell_id in range(0,cell_num): cell = Cell(id="Cell_%i"%cell_id) cell.morphology = generate_with_morphology(db_name,cfg_file) nml_doc.cells.append(cell) pop = Population(id="Pop_%i"%cell_id, component=cell.id, type="populationList") net.populations.append(pop) inst = Instance(id="0") pop.instances.append(inst) inst.location = Location(x=0, y=0, z=0) ####### Write to file ###### nml_file = 'demo_attraction/demo_attraction.net.nml' writers.NeuroMLWriter.write(nml_doc, nml_file) print("Written network file to: "+nml_file) ###### Validate the NeuroML ###### from neuroml.utils import validate_neuroml2 validate_neuroml2(nml_file)
def create_object(name, color, x=0, y=0, z=0): obj = Cell() obj.name = name obj.id = name nml_doc.cells.append(obj) morphology = Morphology(id='mm') obj.morphology = morphology pop = Population(id="Pop_%s" % name, component=obj.id, type="populationList", size=0) net.populations.append(pop) populations[name] = pop pop.properties.append(Property(tag="color", value=color)) add_instance(name, x, y, z) sg = SegmentGroup(id='all') obj.morphology.segment_groups.append(sg) return obj
def create_generic_neuron_cell(self): self.generic_neuron_cell = Cell(id="GenericNeuronCell") morphology = Morphology() morphology.id = "morphology_" + self.generic_neuron_cell.id self.generic_neuron_cell.morphology = morphology prox_point = Point3DWithDiam( x="0", y="0", z="0", diameter=self.get_bioparameter("cell_diameter").value) dist_point = Point3DWithDiam( x="0", y="0", z="0", diameter=self.get_bioparameter("cell_diameter").value) segment = Segment(id="0", name="soma", proximal=prox_point, distal=dist_point) morphology.segments.append(segment) self.generic_neuron_cell.biophysical_properties = BiophysicalProperties( id="biophys_" + self.generic_neuron_cell.id) mp = MembraneProperties() self.generic_neuron_cell.biophysical_properties.membrane_properties = mp mp.init_memb_potentials.append( InitMembPotential( value=self.get_bioparameter("initial_memb_pot").value)) mp.specific_capacitances.append( SpecificCapacitance(value=self.get_bioparameter( "neuron_specific_capacitance").value)) mp.spike_threshes.append( SpikeThresh( value=self.get_bioparameter("neuron_spike_thresh").value)) mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_leak_cond_density").value, id="Leak_all", ion_channel="Leak", erev=self.get_bioparameter("leak_erev").value, ion="non_specific")) mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_k_slow_cond_density").value, id="k_slow_all", ion_channel="k_slow", erev=self.get_bioparameter("k_slow_erev").value, ion="k")) ''' mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("neuron_k_fast_cond_density").value, id="k_fast_all", ion_channel="k_fast", erev=self.get_bioparameter("k_fast_erev").value, ion="k"))''' mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_ca_simple_cond_density").value, id="ca_simple_all", ion_channel="ca_simple", erev=self.get_bioparameter("ca_simple_erev").value, ion="ca")) ip = IntracellularProperties() self.generic_neuron_cell.biophysical_properties.intracellular_properties = ip # NOTE: resistivity/axial resistance not used for single compartment cell models, so value irrelevant! ip.resistivities.append(Resistivity(value="0.1 kohm_cm")) # NOTE: Ca reversal potential not calculated by Nernst, so initial_ext_concentration value irrelevant! species = Species(id="ca", ion="ca", concentration_model="CaPool", initial_concentration="0 mM", initial_ext_concentration="2E-6 mol_per_cm3") ip.species.append(species)
def create_models(self): self.generic_cell = Cell(id = "GenericCell") morphology = Morphology() morphology.id = "morphology_"+self.generic_cell.id self.generic_cell.morphology = morphology prox_point = Point3DWithDiam(x="0", y="0", z="0", diameter=self.get_bioparameter("cell_diameter").value) dist_point = Point3DWithDiam(x="0", y="0", z=self.get_bioparameter("cell_length").value, diameter=self.get_bioparameter("cell_diameter").value) segment = Segment(id="0", name="soma", proximal = prox_point, distal = dist_point) morphology.segments.append(segment) self.generic_cell.biophysical_properties = BiophysicalProperties(id="biophys_"+self.generic_cell.id) mp = MembraneProperties() self.generic_cell.biophysical_properties.membrane_properties = mp mp.init_memb_potentials.append(InitMembPotential(value=self.get_bioparameter("initial_memb_pot").value)) mp.specific_capacitances.append(SpecificCapacitance(value=self.get_bioparameter("specific_capacitance").value)) mp.spike_threshes.append(SpikeThresh(value=self.get_bioparameter("spike_thresh").value)) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("leak_cond_density").value, id="Leak_all", ion_channel="Leak", erev=self.get_bioparameter("leak_erev").value, ion="non_specific")) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("k_slow_cond_density").value, id="k_slow_all", ion_channel="k_slow", erev=self.get_bioparameter("k_slow_erev").value, ion="k")) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("k_fast_cond_density").value, id="k_fast_all", ion_channel="k_fast", erev=self.get_bioparameter("k_fast_erev").value, ion="k")) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("ca_boyle_cond_density").value, id="ca_boyle_all", ion_channel="ca_boyle", erev=self.get_bioparameter("ca_boyle_erev").value, ion="ca")) ip = IntracellularProperties() self.generic_cell.biophysical_properties.intracellular_properties = ip # NOTE: resistivity/axial resistance not used for single compartment cell models, so value irrelevant! ip.resistivities.append(Resistivity(value="0.1 kohm_cm")) # NOTE: Ca reversal potential not calculated by Nernst, so initial_ext_concentration value irrelevant! species = Species(id="ca", ion="ca", concentration_model="CaPool", initial_concentration="0 mM", initial_ext_concentration="2E-6 mol_per_cm3") ip.species.append(species) self.exc_syn = GradedSynapse(id="exc_syn", conductance = self.get_bioparameter("exc_syn_conductance").value, delta = self.get_bioparameter("exc_syn_delta").value, Vth = self.get_bioparameter("exc_syn_vth").value, erev = self.get_bioparameter("exc_syn_erev").value, k = self.get_bioparameter("exc_syn_k").value) self.inh_syn = GradedSynapse(id="inh_syn", conductance = self.get_bioparameter("inh_syn_conductance").value, delta = self.get_bioparameter("inh_syn_delta").value, Vth = self.get_bioparameter("inh_syn_vth").value, erev = self.get_bioparameter("inh_syn_erev").value, k = self.get_bioparameter("inh_syn_k").value) self.elec_syn = GapJunction(id="elec_syn", conductance = self.get_bioparameter("elec_syn_gbase").value) self.offset_current = PulseGenerator(id="offset_current", delay=self.get_bioparameter("unphysiological_offset_current_del").value, duration=self.get_bioparameter("unphysiological_offset_current_dur").value, amplitude=self.get_bioparameter("unphysiological_offset_current").value)
def neuroml_single_cell(skeleton_id, nodes, pre, post): """ Encapsulate a single skeleton into a NeuroML Cell instance. skeleton_id: the ID of the skeleton to which all nodes belong. nodes: a dictionary of node ID vs tuple of node parent ID, location as a tuple of 3 floats, and radius. In nanometers. pre: a dictionary of node ID vs list of connector ID post: a dictionary of node ID vs list of connector ID Returns a Cell with id=skeleton_id. """ # Collect the children of every node successors = defaultdict(list) # parent node ID vs list of children node IDs rootID = None for nodeID, props in nodes.iteritems(): parentID = props[0] if not parentID: rootID = nodeID continue successors[parentID].append(nodeID) # Cache of Point3DWithDiam points = {} def asPoint(nodeID): """ Return the node as a Point3DWithDiam, in micrometers. """ p = points.get(nodeID) if not p: props = nodes[nodeID] radius = props[2] if radius < 0: radius = 0.1 # FUTURE Will have to change loc = props[1] # Point in micrometers p = Point3DWithDiam(loc[0] / 1000.0, loc[1] / 1000.0, loc[2] / 1000.0, radius) points[nodeID] = p return p # Starting from the root node, iterate towards the end nodes, adding a segment # for each parent-child pair. segments = [] segment_id = 1 todo = [rootID] # VERY CONFUSINGLY, the Segment.parent is a SegmentParent with the same id as the parent Segment. An unseemly overheady way to reference the parent Segment. while todo: nodeID = todo.pop() children = successors[nodeID] if not children: continue p1 = asPoint(nodeID) parent = segments[-1] if segments else None segment_parent = SegmentParent(segments=parent.id) if parent else None for childID in children: p2 = asPoint(childID) segment_id += 1 segment = Segment(proximal=p1, distal=p2, parent=segment_parent) segment.id = segment_id segment.name = "%s-%s" % (nodeID, childID) segments.append(segment) todo.append(childID) # Pack the segments into a Cell morphology = Morphology() morphology.segments.extend(segments) morphology.id = "Skeleton #%s" % skeleton_id # Synapses: TODO requires input from Padraig Gleeson cell = Cell() cell.name = 'Cell' cell.id = skeleton_id cell.morphology = morphology return cell
def create_neuron_cell(self, cell_name, morphology): cell = Cell(id = cell_name) cell.notes = "Cell model created by c302 with custom electrical parameters" cell.morphology = morphology cell.biophysical_properties = BiophysicalProperties(id="biophys_"+cell.id) mp = MembraneProperties() cell.biophysical_properties.membrane_properties = mp mp.init_memb_potentials.append(InitMembPotential(value=self.get_bioparameter("initial_memb_pot").value)) mp.specific_capacitances.append(SpecificCapacitance(value=self.get_bioparameter("specific_capacitance").value)) mp.spike_threshes.append(SpikeThresh(value=self.get_bioparameter("neuron_spike_thresh").value)) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("neuron_leak_cond_density").value, id="Leak_all", ion_channel="Leak", erev=self.get_bioparameter("leak_erev").value, ion="non_specific")) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("neuron_k_slow_cond_density").value, id="k_slow_all", ion_channel="k_slow", erev=self.get_bioparameter("k_slow_erev").value, ion="k")) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("neuron_k_fast_cond_density").value, id="k_fast_all", ion_channel="k_fast", erev=self.get_bioparameter("k_fast_erev").value, ion="k")) mp.channel_densities.append(ChannelDensity(cond_density=self.get_bioparameter("neuron_ca_boyle_cond_density").value, id="ca_boyle_all", ion_channel="ca_boyle", erev=self.get_bioparameter("ca_boyle_erev").value, ion="ca")) ip = IntracellularProperties() cell.biophysical_properties.intracellular_properties = ip # NOTE: resistivity/axial resistance not used for single compartment cell models, so value irrelevant! ip.resistivities.append(Resistivity(value=self.get_bioparameter("resistivity").value)) # NOTE: Ca reversal potential not calculated by Nernst, so initial_ext_concentration value irrelevant! species = Species(id="ca", ion="ca", concentration_model="CaPool", initial_concentration="0 mM", initial_ext_concentration="2E-6 mol_per_cm3") ip.species.append(species) return cell
def neuroml_single_cell(skeleton_id, nodes, pre, post): """ Encapsulate a single skeleton into a NeuroML Cell instance. skeleton_id: the ID of the skeleton to which all nodes belong. nodes: a dictionary of node ID vs tuple of node parent ID, location as a tuple of 3 floats, and radius. In nanometers. pre: a dictionary of node ID vs list of connector ID post: a dictionary of node ID vs list of connector ID Returns a Cell with id=skeleton_id. """ # Collect the children of every node successors = defaultdict( list) # parent node ID vs list of children node IDs rootID = None for nodeID, props in nodes.iteritems(): parentID = props[0] if not parentID: rootID = nodeID continue successors[parentID].append(nodeID) # Cache of Point3DWithDiam points = {} def asPoint(nodeID): """ Return the node as a Point3DWithDiam, in micrometers. """ p = points.get(nodeID) if not p: props = nodes[nodeID] radius = props[2] if radius < 0: radius = 0.1 # FUTURE Will have to change loc = props[1] # Point in micrometers p = Point3DWithDiam(loc[0] / 1000.0, loc[1] / 1000.0, loc[2] / 1000.0, radius) points[nodeID] = p return p # Starting from the root node, iterate towards the end nodes, adding a segment # for each parent-child pair. segments = [] segment_id = 1 todo = [rootID] # VERY CONFUSINGLY, the Segment.parent is a SegmentParent with the same id as the parent Segment. An unseemly overheady way to reference the parent Segment. while todo: nodeID = todo.pop() children = successors[nodeID] if not children: continue p1 = asPoint(nodeID) parent = segments[-1] if segments else None segment_parent = SegmentParent(segments=parent.id) if parent else None for childID in children: p2 = asPoint(childID) segment_id += 1 segment = Segment(proximal=p1, distal=p2, parent=segment_parent) segment.id = segment_id segment.name = "%s-%s" % (nodeID, childID) segments.append(segment) todo.append(childID) # Pack the segments into a Cell morphology = Morphology() morphology.segments.extend(segments) morphology.id = "Skeleton #%s" % skeleton_id # Synapses: TODO requires input from Padraig Gleeson cell = Cell() cell.name = 'Cell' cell.id = skeleton_id cell.morphology = morphology return cell
def run(): cell_num = 10 x_size = 500 y_size = 500 z_size = 500 nml_doc = NeuroMLDocument(id="Net3DExample") syn0 = ExpOneSynapse(id="syn0", gbase="65nS", erev="0mV", tau_decay="3ms") nml_doc.exp_one_synapses.append(syn0) net = Network(id="Net3D") nml_doc.networks.append(net) proj_count = 0 #conn_count = 0 for cell_id in range(0,cell_num): cell = Cell(id="Cell_%i"%cell_id) cell.morphology = generateRandomMorphology() nml_doc.cells.append(cell) pop = Population(id="Pop_%i"%cell_id, component=cell.id, type="populationList") net.populations.append(pop) inst = Instance(id="0") pop.instances.append(inst) inst.location = Location(x=str(x_size*random()), y=str(y_size*random()), z=str(z_size*random())) prob_connection = 0.5 for post in range(0,cell_num): if post is not cell_id and random() <= prob_connection: from_pop = "Pop_%i"%cell_id to_pop = "Pop_%i"%post pre_seg_id = 0 post_seg_id = 1 projection = Projection(id="Proj_%i"%proj_count, presynaptic_population=from_pop, postsynaptic_population=to_pop, synapse=syn0.id) net.projections.append(projection) connection = Connection(id=proj_count, \ pre_cell_id="%s[%i]"%(from_pop,0), \ pre_segment_id=pre_seg_id, \ pre_fraction_along=random(), post_cell_id="%s[%i]"%(to_pop,0), \ post_segment_id=post_seg_id, post_fraction_along=random()) projection.connections.append(connection) proj_count += 1 #net.synaptic_connections.append(SynapticConnection(from_="%s[%i]"%(from_pop,0), to="%s[%i]"%(to_pop,0))) ####### Write to file ###### nml_file = 'tmp/net3d.nml' writers.NeuroMLWriter.write(nml_doc, nml_file) print("Written network file to: "+nml_file) ###### Validate the NeuroML ###### from neuroml.utils import validate_neuroml2 validate_neuroml2(nml_file)
def run(): cell_num = 10 x_size = 500 y_size = 500 z_size = 500 nml_doc = NeuroMLDocument(id="Net3DExample") syn0 = ExpOneSynapse(id="syn0", gbase="65nS", erev="0mV", tau_decay="3ms") nml_doc.exp_one_synapses.append(syn0) net = Network(id="Net3D") nml_doc.networks.append(net) proj_count = 0 #conn_count = 0 for cell_id in range(0, cell_num): cell = Cell(id="Cell_%i" % cell_id) cell.morphology = generateRandomMorphology() nml_doc.cells.append(cell) pop = Population(id="Pop_%i" % cell_id, component=cell.id, type="populationList") net.populations.append(pop) pop.properties.append(Property(tag="color", value="1 0 0")) inst = Instance(id="0") pop.instances.append(inst) inst.location = Location(x=str(x_size * random()), y=str(y_size * random()), z=str(z_size * random())) prob_connection = 0.5 for post in range(0, cell_num): if post is not cell_id and random() <= prob_connection: from_pop = "Pop_%i" % cell_id to_pop = "Pop_%i" % post pre_seg_id = 0 post_seg_id = 1 projection = Projection(id="Proj_%i" % proj_count, presynaptic_population=from_pop, postsynaptic_population=to_pop, synapse=syn0.id) net.projections.append(projection) connection = Connection(id=proj_count, \ pre_cell_id="%s[%i]"%(from_pop,0), \ pre_segment_id=pre_seg_id, \ pre_fraction_along=random(), post_cell_id="%s[%i]"%(to_pop,0), \ post_segment_id=post_seg_id, post_fraction_along=random()) projection.connections.append(connection) proj_count += 1 #net.synaptic_connections.append(SynapticConnection(from_="%s[%i]"%(from_pop,0), to="%s[%i]"%(to_pop,0))) ####### Write to file ###### nml_file = 'tmp/net3d.nml' writers.NeuroMLWriter.write(nml_doc, nml_file) print("Written network file to: " + nml_file) ###### Validate the NeuroML ###### from neuroml.utils import validate_neuroml2 validate_neuroml2(nml_file)
def create_cell(): """Create the cell. :returns: name of the cell nml file """ # Create the nml file and add the ion channels hh_cell_doc = NeuroMLDocument(id="cell", notes="HH cell") hh_cell_fn = "HH_example_cell.nml" hh_cell_doc.includes.append(IncludeType(href=create_na_channel())) hh_cell_doc.includes.append(IncludeType(href=create_k_channel())) hh_cell_doc.includes.append(IncludeType(href=create_leak_channel())) # Define a cell hh_cell = Cell(id="hh_cell", notes="A single compartment HH cell") # Define its biophysical properties bio_prop = BiophysicalProperties(id="hh_b_prop") # notes="Biophysical properties for HH cell") # Membrane properties are a type of biophysical properties mem_prop = MembraneProperties() # Add membrane properties to the biophysical properties bio_prop.membrane_properties = mem_prop # Append to cell hh_cell.biophysical_properties = bio_prop # Channel density for Na channel na_channel_density = ChannelDensity(id="na_channels", cond_density="120.0 mS_per_cm2", erev="50.0 mV", ion="na", ion_channel="na_channel") mem_prop.channel_densities.append(na_channel_density) # Channel density for k channel k_channel_density = ChannelDensity(id="k_channels", cond_density="360 S_per_m2", erev="-77mV", ion="k", ion_channel="k_channel") mem_prop.channel_densities.append(k_channel_density) # Leak channel leak_channel_density = ChannelDensity(id="leak_channels", cond_density="3.0 S_per_m2", erev="-54.3mV", ion="non_specific", ion_channel="leak_channel") mem_prop.channel_densities.append(leak_channel_density) # Other membrane properties mem_prop.spike_threshes.append(SpikeThresh(value="-20mV")) mem_prop.specific_capacitances.append(SpecificCapacitance(value="1.0 uF_per_cm2")) mem_prop.init_memb_potentials.append(InitMembPotential(value="-65mV")) intra_prop = IntracellularProperties() intra_prop.resistivities.append(Resistivity(value="0.03 kohm_cm")) # Add to biological properties bio_prop.intracellular_properties = intra_prop # Morphology morph = Morphology(id="hh_cell_morph") # notes="Simple morphology for the HH cell") seg = Segment(id="0", name="soma", notes="Soma segment") # We want a diameter such that area is 1000 micro meter^2 # surface area of a sphere is 4pi r^2 = 4pi diam^2 diam = math.sqrt(1000 / math.pi) proximal = distal = Point3DWithDiam(x="0", y="0", z="0", diameter=str(diam)) seg.proximal = proximal seg.distal = distal morph.segments.append(seg) hh_cell.morphology = morph hh_cell_doc.cells.append(hh_cell) pynml.write_neuroml2_file(nml2_doc=hh_cell_doc, nml2_file_name=hh_cell_fn, validate=True) return hh_cell_fn
def create_neuron_cell(self, cell_name, morphology): cell = Cell(id=cell_name) cell.notes = "Cell model created by c302 with custom electrical parameters" cell.morphology = morphology cell.biophysical_properties = BiophysicalProperties(id="biophys_" + cell.id) mp = MembraneProperties() cell.biophysical_properties.membrane_properties = mp mp.init_memb_potentials.append( InitMembPotential( value=self.get_bioparameter("initial_memb_pot").value)) mp.specific_capacitances.append( SpecificCapacitance( value=self.get_bioparameter("specific_capacitance").value)) mp.spike_threshes.append( SpikeThresh( value=self.get_bioparameter("neuron_spike_thresh").value)) mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_leak_cond_density").value, id="Leak_all", ion_channel="Leak", erev=self.get_bioparameter("leak_erev").value, ion="non_specific")) mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_k_slow_cond_density").value, id="k_slow_all", ion_channel="k_slow", erev=self.get_bioparameter("k_slow_erev").value, ion="k")) mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_k_fast_cond_density").value, id="k_fast_all", ion_channel="k_fast", erev=self.get_bioparameter("k_fast_erev").value, ion="k")) mp.channel_densities.append( ChannelDensity(cond_density=self.get_bioparameter( "neuron_ca_boyle_cond_density").value, id="ca_boyle_all", ion_channel="ca_boyle", erev=self.get_bioparameter("ca_boyle_erev").value, ion="ca")) ip = IntracellularProperties() cell.biophysical_properties.intracellular_properties = ip # NOTE: resistivity/axial resistance not used for single compartment cell models, so value irrelevant! ip.resistivities.append( Resistivity(value=self.get_bioparameter("resistivity").value)) # NOTE: Ca reversal potential not calculated by Nernst, so initial_ext_concentration value irrelevant! species = Species(id="ca", ion="ca", concentration_model="CaPool", initial_concentration="0 mM", initial_ext_concentration="2E-6 mol_per_cm3") ip.species.append(species) return cell