Advanced Tutorial¶
Subclassing Front
¶
Every NeuroDevSim model requires at least one Front
subclass definition with its own manage_front method:
class MyFront(Front):
def manage_front(self,constellation):
This is how specific models are defined, as explained in Getting started. In this subsection additional, optional aspects of subclassing are described in detail.
Additional attributes¶
It may be useful to store data that is needed to control the behavior of fronts or that introduces new dynamics to the model as an additional Front
attribute. There are strict rules on using additional attributes for a Front
subclass:
They need to be declared in the subclass definition using a special syntax shown below.
They are all typed using data types defined in the Python ctypes library or as NeuroDevSim classes. Trying to set an attribute to a value of a different type will throw an error.
They are all fixed size: no dictionaries, lists, strings,…
They will be present in every instance of the subclass, instance specific attributes are not supported (see the Important dos and don’ts). So even if the attribute is functionally needed in only one or a few instances of the subclass, it will be present in all of them. Consider carefully whether an extra attribute is really needed.
The syntax to specify additional attributes is unusual, it is specific to how Python supports memory shared arrays. They are defined as a list of tuples, with each tuple containing the attribute name and and a ctypes
data type:
[('attribute1_name', ctypes_data_type), ('attribute2_name', ctypes_data_type)]
For example, to define an int attribute foo:
class MyFront(Front):
_fields_ = Front._fields_ + [('foo', c_int)]
and a second float attribute bar:
class MyFront(Front):
_fields_ = Front._fields_ + [('foo', c_int), ('bar', c_double)]
Different from standard Python behavior, it is important to respect the types of the additional attributes. The following code will cause a ‘TypeError: int expected instead of float’ because foo has been declared to be an integer:
class MyFront(Front):
_fields_ = Front._fields_ + [('foo', c_int)]
def manage_front(self,constellation):
...
# this will cause an error because 1.5 is a float
self.foo = 1.5
# this is fine
self.foo = int(1.5)
The following ctypes are imported in NeuroDevSim and can be used in attribute definitions:
ctypes type |
Python type |
---|---|
c_bool |
bool |
c_char |
1-character bytes object |
c_short |
int |
c_int |
int |
c_long |
int |
c_double |
float |
The differences between c_short, c_int and c_long is in the number of bytes used (2, 4, 8 bytes on 64 bit operating systems) and the corresponding range of numbers encoded (−32,768 through 32,767; −2,147,483,648 through 2,147,483,647 and -9,223,372,036,854,775,808 through 9,223,372,036,854,775,807). Additional ctypes
data types exist and can be imported by knowledgeable users.
In addition, one can also use a class type, which has been defined elsewhere. At present we recommend using only the predefined Point
class or the ID
class, which is used to identify Fronts
and Substrate
:
class MyFront(Front):
_fields_ = Front._fields_ + [('friend', ID)]
def manage_front(self,constellation):
...
# get a list of fronts with neuron_name beginning with "friend"
my_friends = self.get_fronts(constellation,what='name',name="friend",returnID=True)
# store the first front returned as an ID
self.friend = my_friends[0][0]
Using Fronts
or Substrate
as an additional attribute is not recommended because it makes a copy of the original instance and this copy will not be updated. Moreover, for Front
the specific subclass would have to be specified.
Attribute initialization¶
Additional attributes are automatically initialized to a zero value, in the case of a Structure all its fields are set to zero. An additional attribute can be given a starting value immediately after its instantiation by add_child
:
class MyFront(Front):
_fields_ = Front._fields_ + [('foo', c_int)]
def manage_front(self,constellation):
...
new_front = self.add_child(constellation,new_pos)
new_front.foo = 7
...
Front
status flags¶
Front
status flags are important in scheduling manage_front
calls and controlling model behavior. Some flags report on front status and can only be read, others can be changed by the user and some can be freely used. All flag methods are documented in simulator module. NeuroDevSim tries to keep the control of scheduling simple with the use of the enable(constellation)
and disable(constellation)
methods that directly changes status flags, but in some instances finer control is needed.
Status flags scheduling manage_front
¶
manage_front
is called only for Front
that are active, but the order in which fronts are called depends on the growing and migrating status flags and, if either is set to True, on the position of Front
in the simulation volume. Correct setting of these flags is important to reduce the likelyhood of GridCompetitionError. The active, growing and migrating flags can be set by the user or changed through the enable
and disable
methods:
Flag query |
Set to True |
Set to False |
Active if True |
---|---|---|---|
|
|
|
yes |
|
|
|
yes |
|
|
|
yes |
Note that active changes to True if either growing or migrating are set to True, but the reverse does not apply. All new Front
created by Admin_agent.add_neurons
or Front.add_child
are active and growing. For fronts created by Front.add_branch
only the last of the series made is active and growing unless the optional parameter enable_all is set to True. Somata created by Admin_agent.add_neurons
can also be migrating if the optional parameter is set to True.
Warning
enable
and disable
can be called for any front. Only self or new_front created by add_child
or add_branch
can call set_growing()
, clear_growing()
, set_migrating()
and clear_migrating()
. Calling these methods on other fronts will change the status flags but may not affect their behavior.
As mentioned, using disable(constellation)
to stop growth of self is usually sufficient to control scheduling, but sometimes more fine-grained control is required:
def manage_front(self,constellation):
...
try:
new_front = self.add_child(constellation,new_pos) # make a new front and store it
# make front stop growing but keep it active
self.clear_growing()
return # completed this call
...
In the code example above, self is kept active but is not expected to call add_child
again. This could be useful if self should be able to react to some future condition as it will keep calling manage_front
. If for some reason it should grow again at a later cycle, it is safer to call self.set_growing() first and wait till the next cycle to call add_child
.
In the following code example a parent front is not only enabled again but set to growing after retraction of a child:
def manage_front(self,constellation):
...
parent = self.get_parent(constellation)
parent.enable(constellation,growing=True)
self.retract(constellation) # retract self
return # do not do anything else with self
...
The same effect can be obtained using self.enable_parent(constellation,growing=True)
.
Finally, a reminder: only keep fronts active if needed. Fronts calling manage_front
without it executing any code before return
can slow down simulations significantly. It is possible to disable
fronts transiently using the optional parameters till_cycle, till_cycle_g or till_cycle_m:
def manage_front(self,constellation):
...
self.disable(constellation,till_cycle_g=100)
...
this will disable self till cycle 100. On cycle 100 it becomes active again with is_growing() True.
Read-only status flags¶
The following status flags are set by NeuroDevSim methods and inform on status of Front
:
Flag query |
Set by method |
Meaning of True |
---|---|---|
|
|
shape is cylindrical, False for a sphere |
|
|
soma migrated at previous or current cycle |
|
|
soma migrated at some cycle |
|
|
front has been retracted |
|
|
a child of this front was retracted at some cycle, is reset to False after new child is made |
|
|
front is part of an arc made by
|
These status flags can be read for any Front
but notice that has_moved()
and has_migrated()
can change value during the present cycle and for fronts other than self the timing of this change cannot be predicted.
User availabe status flags¶
The following status flags can be used as boolean variables by the user instead of making a c_bool additional attribute:
Flag query |
Set to True |
Set to False |
---|---|---|
|
|
|
|
|
|
|
|
|
These status flags can be read and set/cleared for any Front
. However, if setting or clearing on a front other than self be sure that only one front can do this during a given cycle to avoid competition. See Changing attributes of Front or Substrate for more information.
Importing a simulation¶
Some developmental models may simulate consecutive stages in development and each stage may be optimized separately during model creation. If the simulations take a lot of time to run it may then be advantageous to use a previous simulation of early stages of development to start simulating a later stage.
This is possible with the Admin_agent.import_simulation
method:
fname_old = "output/prev_simulation.db"
fname = "output/new_simulation.db"
sim_volume = [[0., 0., 0.], [100., 100., 100.]]
neuron_types = [MyFront1,MyFront2]
num_procs = 4
admin = Admin_agent(num_procs,fname,sim_volume,neuron_types,seed=1)
admin.import_simulation(fname_old)
There are strict limitations to the use of import_simulation
:
The database should be importable: this should be set in the previous simulation as
admin.importable_db = True
, best immediately after Admin_agent class initalization.The importing simulation shoul call
import_simulation
as the first method after initalization of Admin_agent class.Admin_agent class initalization should be almost identical to that used for the previous simulation: num_procs, sim_volume and neuron_types should be identical (num_procs can be 0 for Interactive mode). The code defining each neuron type should be compatible with the previous simulation. Optional changes to
Admin_agent
array size attributes should also be identical.The database name for the new simulation should be different from that of the previous one. By default the database contents are copied to the new simulation database, but this can be turned off with the optional copy_db parameter.
Admin_agent
seed and verbose can be different.
The new simulation will start at the next cycle after the last one stored in the previous simulation database. Because it uses an importable database it has all the information needed to continue the simulation as if it was never interrupted. If any Additional attributes were declared their values should be stored during the previous simulation, this can be done with Storing additional attributes and setting the optional parameter last_only=True
.
Several use cases of import_simulation
are shown in the Import notebook.
Interactive mode¶
It is quite easy to run NeuroDevSim in interactive mode, either in a notebook with plotting or from the terminal. However, the interactive mode comes with severe restrictions:
nothing is stored, all results are transient
there is no parallel computing so only simple models can be simulated
a complete simulation is either interactive or not, one cannot switch
Nevertheless the interactive mode can be quite useful to gain intuition, explore ideas and, especially, debug complex models. These use cases are introduced in the Interactive Mode notebook.
Basic interactive simulation¶
All that is needed to run an interactive simulation is to instantiate Admin_agent class with zero num_procs. A Front class needs to be defined because a neuron_type is required. A minimal set-up taken from the notebook example is:
from neurodevsim.simulator import *
class RandomFront(Front):
def manage_front(self,constellation):
pass
if __name__ == '__main__':
# initialize Admin_agent
sim_volume = [[-100., -100., -100.], [100.0,100.0,100.0]]
neuron_types = [RandomFront]
admin = Admin_agent(0,"",sim_volume,neuron_types,verbose=0,plot=True) # interactive mode
constellation = admin.constellation
Notice that the manage_front method is empty, it will not be used. If desired one can have the normal manage_front
present as reference, this may be useful while debugging. Because there is no database output no file name needs to be specified for Admin_agent
.
The Constellation class is obtained because this will be needed in many method calls.
Now any NeuroDevSim method can be called. Because most of these depend on object instantiation, relevant objects should be made first. For example, one could call admin.add_neurons
to make a soma:
fronts = admin.add_neurons(RandomFront,"rand_neuron",1,[[-30.,0.,0.],[-30,0.,0.]],5.)
soma = fronts[0]
Notice that in this case we capture the return of admin.add_neurons
so that the soma that was created can be accessed. Using soma.add_child
more fronts can now be created to simulate simple growth or any other Front
method can be called as desired. Remember that the fronts are not stored in the simulation database and if notebook plotting is enabled, new fronts need to plotted explicitly with admin.plot_item
:
line = admin.plot_item(soma,color='k')
There is no need to call admin.destruction
because no extra processes were spawned.
Interactive model debugging¶
Because NeuroDevSim simulations are not reproducible they can be very difficult to debug in a traditional way. Instead the output of a buggy simulation can be loaded with the import_simulation
method and interactive mode can be used to investigate what went wrong.
To use this approach effectively it is important to identify “problem” fronts. This information can be obtained either by printing out relevant front IDs during the simulation or by analyzing the database content as explained in Understanding the database.
Start an interactive session with the same sim_volume and neuron_types as used for the stored simulation, as shown above. Then import an existing simulation database:
...
admin = Admin_agent(0,"",sim_volume,neuron_types,verbose=0,plot=True) # interactive mode
constellation = admin.constellation
admin.import_simulation("simulation.db")
The complete simulation till the end will be loaded and because plot=True
plotted. For large simulations, plotting takes a lot of time. This can be prevented by plotting only a relevant region of the simulation, using the Admin_agent
box attribute:
admin = Admin_agent(0,"",sim_volume,neuron_types,verbose=0,plot=True,box=[[60.,60.,0.],[90.,90.,30.]])
To use box effectively one should know which region to focus on, usually centered around the “problem” front. It is best to use an isometric box, with idential ax lengths for each dimension. Only when a box is defined, the list of Front
plotted is available as admin.plot_items. If the number of fronts plotted is small, investigating this list is the fastest way to discover what is plotted.
After import_simulation
all fronts that existed at the end of the simulation are present in memory and can be accessed by their ID
.
If “problem” fronts were identified using print statements during the original simulation information like this will have been printed:
Front ID: neuron type 1, index 4005
the corresponding front can now be obtained with:
my_front = constellation.front_by_id(ID(1,4005))
If “problem” fronts were identified in the database, the procedure is a bit more complicated. Each front has two numerical identifiers in the database: a neuron_id and a front_id, see Understanding the database. Combined, these constitute a DataID
which is unfortunately different from ID
, but one can easily be converted into the other using constellation.data_to_id
:
my_ID = constellation.data_to_id(DataID(neuron_id,front_id))
my_front = constellation.front_by_id(my_ID)
Where is my_front in the simulation plot? As this is often not easy to recognize, one can make any plotted front flash during an interactive session:
admin.flash_front(my_front)
If the plot is crowded, it may have to be rotated before the flashes are visible. One can flash_front
as often as necessary.
The next steps depend on the type of problem to solve. Let’s, for example, look at a fatal collison during an add_child
call:
new_pos = ...
try:
new_front = my_front.add_child(constellation,new_pos)
print (new_front)
except CollisionError as error:
print (error)
colerror = error # error is only defined within the except scope
Either a new_front will be created and printed or a CollisionError
occurs and then this will be printed. If the error occurred, one could then try solve_collision
:
points = my_front.solve_collision(constellation,new_pos,colerror)
print (len(points),"points:",points)
If solve_collision
fails (it returns an empty list), maybe this is due to multiple colliding structures? By default only the first collision is detected as explained in Preventing and dealing with collisions, but this can be changed so that all collisions are detected:
constellation.only_first_collision = False # report all collisions
colerror = None
try:
new_front = my_front.add_child(constellation,new_pos)
print (new_front)
except CollisionError as error:
print (error)
colerror = error # error is only defined within the except scope
if colerror:
for f in colerror.collider: # print all the colliding structures
print (f)
The possibilities of the interactive mode are endless… One can test large sections of code, the behavior of specific methods, explore alternatives, etc. Just remember that nothing gets stored!
Finally, when investigating Modeling soma migration, the interactive mode has an additional useful feature: it can provide a history of the migration of any migrating soma that was loaded by import_simulation
with get_migration_history
:
my_somaID = constellation.data_to_id(DataID(neuron_id,front_id))
my_soma = constellation.front_by_id(my_somaID)
coordinates, cycles = my_soma.get_migration_history(constellation)
coordinates is a list of Point
representing my_soma.orig, from the location where it was created up to the last cycle, and cycles contains the corresponding cycle for each entry in coordinates. One can print this information or plot it:
lines = admin.plot_item(coordinates,color='k',line=True)
Understanding the database¶
All NeuroDevSim simulations are saved in a SQLite database. Many SQLite database browser apps are available to view the contents of the database, we like the DB Browser for SQLite.
NeuroDevSim comes with a set of methods in the processing module that print or plot database contents, so most users may never have to read this section of the manual. But understanding of the database structure is necessary to write specific analysis routines and may also be required to use the Interactive mode effectively.
Database tables¶
Information is stored in different database tables. These tables are shown in order of relevance for the user below and important tables are discussed in more detail next. Optional tables are tables that are only made when relevant: either when the corresponding class is instantiated or when Storing additional attributes has been used.
Table name |
Optional |
Description |
---|---|---|
neuron_data |
no |
data for every Neuron in simulation, necessary for front_data |
front_data |
no |
data for every Front in simulation, needed for synapse_data, migration_data1 |
synapse_data |
yes |
data for every Synapse in simulation |
substrate_data |
yes |
data for all Substrate in simulation |
migration_data1 |
yes |
coordinates of migrating somata, additional migration_data2,… may be present |
neurodevsim |
no |
basic information about simulation: volume, number cycles, software version,… |
neuron_types |
no |
class names of each neuron_type in simulation |
attributes |
yes |
list of all tables storing additional attributes (tables not listed here) |
arc_data |
yes |
technical table: data for all Arcs, only needed for import_simulation |
arc_points |
yes |
technical table: points for all Arcs, only needed for import_simulation |
mig_fronts_data |
yes |
technical table: order of migrating fronts, only needed for import_simulation |
sqlite_sequence |
no |
technical table: standard sqlite table listing all other tables and their length |
The contents of the database are updated at the end of each cycle. If a simulation crashes or is stopped, the database will be readable and contain correct information up till the last complete cycle. However, changing content in several tables is only updated at the end of the simulation by Admin_agent.destruction
.
Reading the database¶
This subsection will be familiar to anybody who has previously written code to read from a SQLite database. One connects to the database using its filename db_name and creates a cursor:
import sqlite3
...
conn = sqlite3.connect(db_name)
cursor = conn.cursor()
conn.row_factory = sqlite3.Row
The last statement is quite important because it allows to access content by the name of columns, which results in more readable and easier to manage code.
Next one usually loads an entire table this_table and analyzes it row by row:
cursor.execute("select * from this_table")
result = cursor.fetchall()
for row in result:
item1 = row['name_of_column2']
item2 = row['name_of_column3']
...
Each table has as first column an id created by SQLite itself, this id is not related to the simulation. The content of relevant tables is described briefly in the following subsections, but id will not be mentioned. To get started yourself, it maybe helpful to look at the code in processing module, for example the nds_neuron
method is fairly easy to understand.
NeuroDevSim writes new data to the database at the end of every cycle, so its information is complete up to the last completed cycle in case of a crash of the simulation. Type refers to SQLite data types, not NeuroDevSim data types. Updated refers to whether the value may be updated from its initial value when the object was created.
neuron_data table:¶
This table contains a row for each Neuron
created and (a) new row(s) will be written to the database at the end of each admin.add_neurons
call, it has the following columns:
Column_name |
Type |
Description |
Updated |
---|---|---|---|
neuron_id |
int |
unique to each neuron, is the neuron identifier in |
no |
type_id |
int |
identifies neuron_type, is the neuron identifier in |
no |
name |
text |
name of the neuron |
no |
firing_rate |
real |
initial firing_rate of neuron |
no |
CV_ISI |
real |
initial CV_ISI of neuron |
no |
num_fronts |
int |
final number of fronts in neuron |
at end |
num_retracted |
int |
final number of retracted fronts in neuron |
at end |
num_synapses |
int |
final number of synapses in neuron |
at end |
‘at end’ means that the column value is updated by admin.destruction()
.
front_data table:¶
This table contains a row for each Front
created, with the following columns:
Column_name |
Type |
Description |
Updated |
---|---|---|---|
neuron_id |
int |
neuron the front belongs to, refers to similar column in neuron_data |
no |
front_id |
int |
unique to each front of a specific neuron_type,
front identifier in |
no |
branch |
text |
optional branch_name of front |
no |
swc_type |
int |
swc_type of the front, see SWC types used in NeuroDevSim |
no |
shape |
int |
indicates spherical (shape == 1) or cylindrical (shape == 2) front |
no |
orig_x |
real |
x of the orig coordinate of the front, for a migrating soma this is its original position |
no |
orig_y |
real |
y of the orig coordinate of the front |
no |
orig_z |
real |
z of the orig coordinate of the front |
no |
end_x |
real |
x of the end coordinate of the front, for spherical fronts identical to their orig |
no |
end_y |
real |
y of the end coordinate of the front |
no |
end_z |
real |
z of the end coordinate of the front |
no |
radius |
real |
front radius |
no |
parent_id |
int |
the front_id of the parent front or -1 for the root of a neuron tree |
maybe |
b_order |
int |
branching order of the front, 0 at the root |
no |
path_len |
real |
cumulated path_length from soma till end of the front |
no |
birth |
int |
birth of the front, cycle when the front was created |
no |
death |
int |
death, -1 or if retracted cycle when the front was retracted |
maybe |
migration |
int |
column number in migration_data table for migrating soma, 0 if not migrating |
no |
flags |
int |
maybe |
The death column value is updated at the end of the cycle of retraction, otherwise it stays -1. The parent_id (may have changed due to Modeling soma migration) and flags columns are updated at the end of simulation by admin.destruction()
if admin.importable_db == True
, otherwise they keep the original value.
Note that num_children and the parent to child relation are not stored in the database. This information is implicit in the child to parent relation that is stored in the parent_id column.
synapse_data table:¶
This table contains a row for each Synapse
created, with the following columns:
Column_name |
Type |
Description |
Updated |
---|---|---|---|
pre_neuron_id |
int |
identifies presynaptic neuron, refers to neuron_id in neuron_data |
no |
pre_front_id |
int |
identifies presynaptic front, refers front_id in front_data |
no |
post_neuron_id |
int |
identifies postsynaptic neuron, refers to neuron_id in neuron_data |
no |
post_front_id |
int |
identifies postsynaptic front, refers front_id in front_data |
no |
weight: |
real |
initial synaptic weight |
no |
birth |
int |
birth of the synapse, cycle when the synapse was created |
no |
death |
int |
death, -1 or if removed cycle when synapse was removed |
maybe |
The death column value is updated at the end of the cycle of removal, otherwise it stays -1.
substrate_data table:¶
This table contains a row for each Substrate
created, with the following columns:
Column_name |
Type |
Description |
Updated |
---|---|---|---|
name |
text |
name of the substrate |
no |
x |
real |
x of the orig coordinate of the substrate |
no |
y |
real |
y of the orig coordinate of the substrate |
no |
z |
real |
z of the orig coordinate of the substrate |
no |
amount |
real |
n_mol of the substrate |
no |
rate |
real |
rate of the substrate |
no |
diff_c |
real |
diff_c of the substrate |
no |
birth |
int |
birth of the substrate, cycle when the substrate was created |
no |
death |
int |
death -1 |
no |
At present Substrate
cannot be removed.
migration_data table:¶
The database can contain several migration tables, numbered consecutively as migration_data1, migration_data2, migration_data3,… This is because the number of columns in a SQLite database table is limited, so if more than 600 somata migrated an extra migration_data table will be created.
These tables contains a row for each cycle during which a migration event took place. Its first column is the cycle and then 3 columns for each migrating soma:
Column_name |
Type |
Description |
Updated |
---|---|---|---|
cycle |
int |
the cycle at which each soma migrated to these positions |
no |
x_… |
real |
for each front identified by the elements of |
no |
y_… |
real |
same for the y position of the coordinate |
no |
z_… |
real |
same for the z position of the coordinate |
no |
If multiple migration_data table are present the cycle column of each table is unique.
neurodevsim table¶
This table contains only a single row with information about the simulation in the following columns:
Column_name |
Type |
Description |
Updated |
---|---|---|---|
xmin |
int |
x of the left-front-bottom coordinate of the simulation volume |
no |
ymin |
int |
y of the left-front-bottom coordinate of the simulation volume |
no |
zmin |
int |
z of the left-front-bottom coordinate of the simulation volume |
no |
xmax |
int |
x of the right-back-top coordinate of the simulation volume |
no |
ymax |
int |
y of the right-back-top coordinate of the simulation volume |
no |
zmax |
int |
z of the right-back-top coordinate of the simulation volume |
no |
num_cycles |
int |
total number of cycles simulated |
at end |
num_procs |
int |
number of computing processes used to instantiate |
no |
version |
real |
value representing the NeuroDevSim version number multiplied by 100., used by many methods to check whether database can be read |
no |
run_time |
real |
run time of the simulation in seconds |
at end |
importable |
int |
can database be read by |
at end |
substrate |
int |
number of substrate tables present in the database (0 or 1) |
yes |
migration |
int |
number of migration tables present in the database |
yes |
synapses |
int |
number of synapses tables present in the database (0 or 1) |
yes |
attributes |
int |
number of tables storing attributes present in the database |
yes |
arcs |
int |
number of arc related tables present in the database (0 or 2) |
at end |
‘at end’ means that the column value is updated by admin.destruction()
.
neuron_types table¶
This table contains a row for each Front
subclass listed in neuron_types during instantiation of Admin_agent class.
Column_name |
Type |
Description |
Updated |
---|---|---|---|
type_id |
int |
index into shared arrays, is the neuron identifier in |
no |
neuron_type |
text |
class name of |
no |
attributes table¶
This table contains a row for each attribute that was stored using admin.attrib_to_db
, it is updated by this method.
Column_name |
Type |
Description |
Updated |
---|---|---|---|
name |
text |
name of the attribute table |
no |
type |
text |
SQLite type of the attribute |
no |
neuron_name |
text |
neuron_name optional parameter of |
no |
last_only |
int |
last_only optional parameter of |
no |
Attribute data tables¶
Several such tables may be present with names listed in the attributes table: the name of the attribute followed by _data. It contents depend on the SQLite type of the attribute.
Column_name |
Type |
Description |
Updated |
---|---|---|---|
neuron_id |
int |
identifies neuron, refers to neuron_id column in neuron_data |
no |
front_id |
int |
identifies front if relevant (0 for |
no |
cycle |
int |
simulation cycle for which data is stored |
no |
1, 2 or 3 columns: |
|||
attribute name |
int |
integer value of a simple attribute |
no |
or |
|||
attribute name |
real |
real value of a simple attribute |
no |
or |
|||
attribute name |
text |
text value of a simple attribute |
no |
or |
|||
attribute_ID_0 |
int |
type_id component of an |
no |
attribute_ID_1 |
int |
front_id component of an |
no |
or |
|||
attribute_x |
real |
x value of a |
no |
attribute_y |
real |
y value of a |
no |
attribute_z |
real |
z value of a |
no |
Database from a crashed simulation¶
The database of a crashed simulation will be intact but incomplete and never importable.
All tables will have up to date information till the cycle before the crash, but none of the data marked as Updated at end in the table listings will have been updated. The easiest way to identify a database as being from a crashed simulation is to check the num_cycles column in the neurodevsim table: it will be 0 (except after import_simulation, then it will be final cycle of the imported database). The number of cycles stored can be determined by the birth of the last fronts stored in the front_data table.
Storing additional attributes¶
It may be useful or necessary to store attributes that are not automatically stored by NeuroDevSim either because they need to be analyzed or plotted, or to prepare for Importing a simulation. These may either be user defined Additional attributes or attributes like Neuron
firing_rate for which only the initial value is stored by default.
The admin.attrib_to_db
method supports such storing. attrib_to_db
is called in the main part of the code, anytime after at least one instance of the Front
subclass has been made, for example by add_neurons
. This code sample causes Front.num_children
to be stored for all instances of SimpleNeuronFront
:
if __name__ == '__main__':
...
neuron_types = [SimpleNeuronFront,AxonFront]
admin = Admin_agent(...)
admin.add_neurons(SimpleNeuronFront,"simple_neuron",1,[[...],[...]],30.)
admin.attrib_to_db(SimpleNeuronFront,"num_children","int")
attrib_to_db
requires minimally 3 parameters: the subclass of ``Front`` for which the attribute should be stored, the name of the attribute to be stored and a sql_type. The latter defines the format that should be used to store the parameter in the database and should be one of the following:
sql_type |
ctypes or class |
columns in the data table |
---|---|---|
int |
c_bool, c_short, c_int, c_long |
1 int column containing the value |
real |
c_double |
1 real column containing the value |
text |
c_char |
1 text column containing the text |
id |
|
2 int columns containing both parts |
point |
|
3 real columns containing x, y, z |
An example of storing an additional attribute:
class SimpleNeuronFront(SynFront):
_fields_ = SynFront._fields_ + [('signal', c_double)]
...
if __name__ == '__main__':
...
neuron_types = [SimpleNeuronFront,AxonFront]
admin = Admin_agent(...)
admin.add_neurons(SimpleNeuronFront,"integrate_neuron",1,[[...],[...]],30.)
admin.attrib_to_db(SimpleNeuronFront,"signal","real")
attrib_to_db
will only store attributes that are not stored yet or that are not being updated. Trying to store an unchanging attribute that is always stored, like for example Front.radius
or Neuron.neuron_name
will cause an error.
By default attrib_to_db
assumes that the attribute to store belongs to Front
, but for Neuron
firing_rate and CV_ISI and for Synapse
weight can be stored but the class needs to be specified in the object optional parameter:
admin.attrib_to_db(AxonFront,"firing_rate","real",object=Neuron)
admin.attrib_to_db(SimpleNeuronFront,"weight","real",object=Synapse)
If multiple attributes from the same Front
subclass or object are to be stored they can be specified as a list:
admin.attrib_to_db(AxonFront,["firing_rate","CV_ISI"],["real","real"],object=Neuron)
Note that sql_type then also becomes a list, because not all attributes may have the same type. It is not possible to combine attributes from different object types in a list.
Another optional parameter controls how often the attribute is saved. By default it is saved for every cycle after the attrib_to_db
call, but this may not be necessary. If the attribute is stored to prepare for Importing a simulation then only its final value is needed. This can be achieved by setting the last_only optional parameter to True:
if __name__ == '__main__':
...
neuron_types = [SimpleNeuronFront,AxonFront]
admin = Admin_agent(...)
admin.importable_db = True
admin.add_neurons(AxonFront,"axon",10,[[...],[...]],1.,axon=[...])
admin.attrib_to_db(AxonFront,"goalID","id",last_only=True)
goaldID will now only be stored for the last cycle of the simulation.
The last optional parameter of attrib_to_db
controls for which neurons data will be stored. The neuron_name optional parameter will limit storage to data belonging to neurons with this name (neuron_name is a wildcard).
Several use cases of attrib_to_db
are shown in the Database notebook, this also includes analysis and plotting of stored data. An Importing a simulation use case is shown in the Import notebook.