diff --git a/.gitignore b/.gitignore index ef54fa2..030f3f6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ examples/**/output/log.txt examples/**/output/config.json .idea .cache - +**/mechanisms/x86_64 +.ipynb_checkpoints/ diff --git a/src/pysonata/README.md b/src/pysonata/README.md index 2d8f66d..a6c5de6 100644 --- a/src/pysonata/README.md +++ b/src/pysonata/README.md @@ -25,5 +25,4 @@ Note: ## Reading in Data Files (Nodes and Edges) -*Please stay tuned...(although the sonata/tests/ directory has some good examples)* - +* [pySONATA Tutorial](docs/Tutorial%20-%20pySONATA.ipynb) diff --git a/src/pysonata/docs/Tutorial - pySONATA.ipynb b/src/pysonata/docs/Tutorial - pySONATA.ipynb new file mode 100644 index 0000000..7a22110 --- /dev/null +++ b/src/pysonata/docs/Tutorial - pySONATA.ipynb @@ -0,0 +1,722 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# pySONATA\n", + "A python based api for working with SONATA based files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import glob\n", + "import pprint\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "from sonata.io import File" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reading SONATA Network files" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Contains nodes: True\n", + "Contains edges: False\n" + ] + } + ], + "source": [ + "# Use one network file\n", + "net = File(data_files='../../../examples/300_cells/network/internal_nodes.h5', \n", + " data_type_files='../../../examples/300_cells/network/internal_node_types.csv')\n", + "print('Contains nodes: {}'.format(net.has_nodes))\n", + "print('Contains edges: {}'.format(net.has_edges))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Contains nodes: True\n", + "Contains edges: True\n" + ] + } + ], + "source": [ + "# Read in mulitple files at a time\n", + "net = File(data_files=['../../../examples/300_cells/network/internal_nodes.h5', \n", + " '../../../examples/300_cells/network/internal_internal_edges.h5'], \n", + " data_type_files=['../../../examples/300_cells/network/internal_node_types.csv',\n", + " '../../../examples/300_cells/network/internal_internal_edge_types.csv'])\n", + "print('Contains nodes: {}'.format(net.has_nodes))\n", + "print('Contains edges: {}'.format(net.has_edges))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with nodes \n", + "SONATA files are divided into nodes (eg cells) and edges (eg synapses, junctions). The Nodes themselves are further divides into different **populations**. Different populations can be assumed to have been indepently created, with their own independent set of node ids, models, etc.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node populations in File: [u'internal']\n" + ] + } + ], + "source": [ + "file_nodes = net.nodes # Get the /nodes/ root \n", + "\n", + "# In this example there is only one node population that we've called \"internal\"\n", + "print('Node populations in File: {}'.format(net.nodes.population_names))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Population \"internal\" contains 300 nodes\n", + "node ids: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17\n", + " 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35\n", + " 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53\n", + " 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71\n", + " 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89\n", + " 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107\n", + " 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125\n", + " 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143\n", + " 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161\n", + " 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179\n", + " 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197\n", + " 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215\n", + " 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233\n", + " 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251\n", + " 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269\n", + " 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287\n", + " 288 289 290 291 292 293 294 295 296 297 298 299]\n" + ] + } + ], + "source": [ + "# Get Node populations with the name internal, This population contains 300 individual nodes \n", + "internal_nodes = net.nodes['internal'] \n", + "print('Population \"internal\" contains {} nodes'.format(len(internal_nodes)))\n", + "#pprint.pprint(internal_nodes.node_ids)\n", + "print ('node ids: {}'.format(internal_nodes.node_ids))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use to *to_dataframe* method to quickly analyze the node data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
node_type_idnode_idrotation_angle_yaxisxyzeimodel_processingmorphologymodel_templatemodel_typemodel_name
010000.98654222.343432-79.627003-1.534002eaibs_perisomaticScnn1a_473845048_mnml:Cell_472363762.cell.nmlbiophysicalScnn1a
110012.221220-2.594341-43.37611824.234707eaibs_perisomaticScnn1a_473845048_mnml:Cell_472363762.cell.nmlbiophysicalScnn1a
210023.90694224.31001248.65292211.593498eaibs_perisomaticScnn1a_473845048_mnml:Cell_472363762.cell.nmlbiophysicalScnn1a
310030.2697377.004998-85.896637-4.738925eaibs_perisomaticScnn1a_473845048_mnml:Cell_472363762.cell.nmlbiophysicalScnn1a
410042.95010126.71627515.561911-23.239033eaibs_perisomaticScnn1a_473845048_mnml:Cell_472363762.cell.nmlbiophysicalScnn1a
\n", + "
" + ], + "text/plain": [ + " node_type_id node_id rotation_angle_yaxis x y \\\n", + "0 100 0 0.986542 22.343432 -79.627003 \n", + "1 100 1 2.221220 -2.594341 -43.376118 \n", + "2 100 2 3.906942 24.310012 48.652922 \n", + "3 100 3 0.269737 7.004998 -85.896637 \n", + "4 100 4 2.950101 26.716275 15.561911 \n", + "\n", + " z ei model_processing morphology \\\n", + "0 -1.534002 e aibs_perisomatic Scnn1a_473845048_m \n", + "1 24.234707 e aibs_perisomatic Scnn1a_473845048_m \n", + "2 11.593498 e aibs_perisomatic Scnn1a_473845048_m \n", + "3 -4.738925 e aibs_perisomatic Scnn1a_473845048_m \n", + "4 -23.239033 e aibs_perisomatic Scnn1a_473845048_m \n", + "\n", + " model_template model_type model_name \n", + "0 nml:Cell_472363762.cell.nml biophysical Scnn1a \n", + "1 nml:Cell_472363762.cell.nml biophysical Scnn1a \n", + "2 nml:Cell_472363762.cell.nml biophysical Scnn1a \n", + "3 nml:Cell_472363762.cell.nml biophysical Scnn1a \n", + "4 nml:Cell_472363762.cell.nml biophysical Scnn1a " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "internal_nodes.to_dataframe().head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can use the *get_node_id* method to fetch information about a single node. Each node will be returned as a *sonata.Node* object with attributes and properties that can be accessed like a dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "class: \n", + "node_id: 1\n", + "node props: {'node_type_id': 100, 'ei': 'e', y: -43.37611757606959, 'model_processing': 'aibs_perisomatic', 'model_type': 'biophysical', 'node_id': 1, 'model_template': 'nml:Cell_472363762.cell.nml', x: -2.5943407870091946, 'morphology': 'Scnn1a_473845048_m', rotation_angle_yaxis: 2.2212202206885547, z: 24.23470695483876, 'model_name': 'Scnn1a'}\n", + "(-2.5943407870091946, -43.37611757606959, 24.23470695483876)\n" + ] + } + ], + "source": [ + "node1 = internal_nodes.get_node_id(1) # Get node in population with node_id = 1\n", + "print('class: {}'.format(type(node1)))\n", + "print('node_id: {}'.format(node1.node_id))\n", + "print('node props: {}'.format(node1))\n", + "\n", + "coords = (node1['x'], node1['y'], node1['z'])\n", + "print(coords)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also use the *get_row* method to fetch the i'th row in the table. But it's important to note that node_id's do not have to be ordered or contigous - so the i'th row may not always contain node with node_id i." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "node_row1 = internal_nodes.get_row(1)\n", + "print(node1.node_id == node_row1.node_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating through Nodes\n", + "To iterate through all nodes in the population:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node #127 has maximum distance 119.942558428um from origin\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "max_dist = 0.0\n", + "max_dist_node = None\n", + "for node in internal_nodes:\n", + " assert(0 <= node.node_id < 300)\n", + " ndist = np.sqrt(node['x']**2 + node['y']**2 + node['z']**2)\n", + " if ndist > max_dist:\n", + " max_dist = ndist\n", + " max_dist_node = node\n", + "\n", + "print('Node #{} has maximum distance {}um from origin'.format(max_dist_node.node_id, max_dist)) \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using the get_rows method we can pass in a list or range of the rows we want to analyze" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "processed 150 nodes\n", + "processed 3 nodes\n" + ] + } + ], + "source": [ + "n_count = 0\n", + "for node in internal_nodes.get_rows(range(0, len(internal_nodes), 2)):\n", + " n_count += 1\n", + " assert(node.node_id % 2 == 0)\n", + "print('processed {} nodes'.format(n_count))\n", + "\n", + "n_count = 0\n", + "for node in internal_nodes.get_rows([0, 100, 200]):\n", + " assert(node.node_id % 100 == 0)\n", + " n_count += 1\n", + "print('processed {} nodes'.format(n_count))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Filtering by attributes\n", + "Use the filter method to fetch nodes with certain properties" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 80 nodes that use Nr5a1_471087815_m morphology\n" + ] + } + ], + "source": [ + "n_count = 0\n", + "for node in internal_nodes.filter(morphology='Nr5a1_471087815_m'):\n", + " assert(node['morphology'] == 'Nr5a1_471087815_m')\n", + " n_count += 1\n", + " \n", + "print('Found {} nodes that use Nr5a1_471087815_m morphology'.format(n_count))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Vectorized node attributes\n", + "\n", + "SONATA stores all data into one or more **node groups**. Every node belongs to one node group, with every node group containing the same attributes. Thus nodes with different models (and therefore different attributes) will be split into different groups. To show the available node_groups:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model groups in internal_nodes population: [0]\n", + "Group 0 nodes contains the following attributes \n", + "\t [rotation_angle_yaxis, x, y, z, ei, model_processing, morphology, model_template, model_type, model_name]\n" + ] + } + ], + "source": [ + "# In this example there is only one node-group, group #0\n", + "print('Model groups in internal_nodes population: {}'.format(internal_nodes.group_ids))\n", + "\n", + "grp0 = internal_nodes.get_group(internal_nodes.group_ids[0])\n", + "print('Group {} nodes contains the following attributes \\n\\t {}'.format(grp0.group_id, grp0.all_columns))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instead of looping through and finding attributes of every individual node, we can use the *get_values* method to get all attributes at once as a list or numpy array. This allows us to use vectorization to speed up our analysis:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node #127 has maximum distance 119.942558428um from origin\n" + ] + } + ], + "source": [ + "distances = np.sqrt(grp0.get_values('x')**2 + grp0.get_values('y')**2 + grp0.get_values('z')**2)\n", + "max_indx = np.argmax(distances)\n", + "print('Node #{} has maximum distance {}um from origin'.format(grp0.node_ids[max_indx], distances[max_indx])) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with edges\n", + "\n", + "Edges share a similar data structure as our nodes. Within a file there may be zero, one, or more edge populations each identified by a unique user defined population name." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Edge populations in file: [u'internal_to_internal']\n" + ] + } + ], + "source": [ + "file_edges = net.edges\n", + "print('Edge populations in file: {}'.format(file_edges.population_names))\n", + "recurrent_edges = file_edges['internal_to_internal']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Every edge population contains directed connections between the nodes of one node-population to another. The attributes *target_population* and *source_population* will be important to determine what set of nodes will be used for pre-synaptic/post-synaptic connections:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Edge population internal_to_internal contains 88316 connections from \"internal\" --> \"internal\"\n" + ] + } + ], + "source": [ + "# In this case both the source and target node population are the same indicating these are recurrent connections\n", + "print('Edge population {} contains {} connections from \"{}\" --> \"{}\"'.format(recurrent_edges.name, \n", + " len(recurrent_edges),\n", + " recurrent_edges.source_population, \n", + " recurrent_edges.target_population))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating over edges\n", + "\n", + "When we iterate over all the edges in the edge population we are returned Edge objection. Every edge must contain a source (pre-synaptic) and target (post-synaptic) node-id, but also contain edge attributes which we fetch like a dictionary:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 --> 0\n", + "{pos_z: 276.13055401859, pos_x: 60.45881079218742, pos_y: 3.356650750251845, 'dynamics_params': 'AMPA_ExcToExc.json', 'edge_type_id': 100, 'target_query': \"ei=='e'\", 'delay': 2.0, syn_weight: 6e-05, 'model_template': 'Exp2Syn', sec_id: 28, sec_x: 0.5, dist: 98.29584776371554, 'source_query': \"ei=='e'\", type: 3}\n" + ] + } + ], + "source": [ + "for edge in recurrent_edges:\n", + " print('{} --> {}'.format(edge.source_node_id, edge.target_node_id))\n", + " print edge\n", + " assert(isinstance(edge['syn_weight'], float))\n", + " \n", + " break # Iterating through all the edges can take a long time, do so at your own risk" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Often we may want to get only those edges to connection to/from a given node or set of nodes. SONATA uses special indexing, so selecting by target or source can be achieved signficantly faster than iterating through all possible edges. To find all edges with a given target (or list of targets) we use the *get_target* (*get_targets*) method. :" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 182 connections onto target node #100\n" + ] + } + ], + "source": [ + "con_count = 0\n", + "for edge in recurrent_edges.get_target(100): # we can also use get_targets([id0, id1, ...])\n", + " assert(edge.target_node_id == 100)\n", + " con_count += 1\n", + " \n", + "print('There are {} connections onto target node #{}'.format(con_count, 100))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or we can search for edges by source_node_id" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The first 5 nodes have on average 190.6 outgoing connections\n" + ] + } + ], + "source": [ + "con_count = 0\n", + "for edge in recurrent_edges.get_targets(range(5)):\n", + " assert(edge.target_node_id < 5)\n", + " con_count += 1\n", + " \n", + "print('The first 5 nodes have on average {} outgoing connections'.format(con_count/5.0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Filtering by attributes\n", + "\n", + "We can use the *filter* method to find only those edges with a given matching attribute value:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "45610 out of 88316 edges use AMPA_ExcToExc parameters\n" + ] + } + ], + "source": [ + "# may take some time to complete\n", + "n_ampa = sum(1 for _ in recurrent_edges.filter(dynamics_params='AMPA_ExcToExc.json'))\n", + "print('{} out of {} edges use AMPA_ExcToExc parameters'.format(n_ampa, len(recurrent_edges)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:bmtk-py27]", + "language": "python", + "name": "conda-env-bmtk-py27-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/pysonata/sonata/io/edge.py b/src/pysonata/sonata/io/edge.py index 75a3d00..4b2de9e 100644 --- a/src/pysonata/sonata/io/edge.py +++ b/src/pysonata/sonata/io/edge.py @@ -86,5 +86,13 @@ def __getitem__(self, prop_key): else: raise KeyError('Property {} not found in edge.'.format(prop_key)) + def __repr__(self): + return self.__str__() + + def __str__(self): + ret_dict = self._edge_type_props.copy() + ret_dict.update(self._group_props) + return str(ret_dict) + def __contains__(self, prop_key): return prop_key in self._group_props or prop_key in self._edge_type_props diff --git a/src/pysonata/sonata/io/group.py b/src/pysonata/sonata/io/group.py index 32a4387..fbb784f 100644 --- a/src/pysonata/sonata/io/group.py +++ b/src/pysonata/sonata/io/group.py @@ -155,7 +155,6 @@ def __init__(self, group_id, h5_group, parent): @property def node_ids(self): self.build_indicies() - # print self._parent_indicies return self._parent.inode_ids(self._parent_indicies) @property diff --git a/src/pysonata/sonata/io/node.py b/src/pysonata/sonata/io/node.py index 4fa24ae..50d9260 100644 --- a/src/pysonata/sonata/io/node.py +++ b/src/pysonata/sonata/io/node.py @@ -124,3 +124,13 @@ def __getitem__(self, prop_key): def __contains__(self, prop_key): return prop_key in self._group_props or prop_key in self._node_type_props + + def __repr__(self): + return self.__str__() + + def __str__(self): + ret_dict = self._node_type_props.copy() + ret_dict.update(self._group_props) + ret_dict['node_id'] = self.node_id + ret_dict['node_type_id'] = self.node_type_id + return str(ret_dict) diff --git a/src/pysonata/sonata/io/population.py b/src/pysonata/sonata/io/population.py index 0edebd2..d525262 100644 --- a/src/pysonata/sonata/io/population.py +++ b/src/pysonata/sonata/io/population.py @@ -237,7 +237,13 @@ def add_gids(self, gid_map_df, force=False): self._has_gids = True def to_dataframe(self): - raise NotImplementedError + if len(self.groups) == 1: + return self.get_group(self.group_ids[0]).to_dataframe() + else: + dataframes = [] + for grp_id in self.group_ids: + dataframes.append(self.get_group(grp_id)).to_datframe() + return dataframes def get_row(self, row_indx): # TODO: Use helper function so we don't have to lookup gid/node_id twice @@ -399,13 +405,23 @@ def get_target_population(pop_group_h5): def edge_types_table(self): return self._types_table + @property + def indices_group(self): + if 'indices' in self._pop_group: + return self._pop_group['indices'] + elif 'indicies' in self._pop_group: + # spelling mistake, keeping in for backwards compatibility + return self._pop_group['indicies'] + else: + return None + def to_dataframe(self): raise NotImplementedError def build_indicies(self): - if 'indicies' in self._pop_group: - indicies_grp = self._pop_group['indicies'] - for index_name, index_grp in indicies_grp.items(): + indices_grp = self.indices_group + if indices_grp is not None: + for index_name, index_grp in indices_grp.items(): # TODO: Let __IndexStruct build the indicies # Make sure subgroup has the correct datasets if not isinstance(index_grp, h5py.Group): diff --git a/src/pysonata/sonata/tests/examples/lgn_node_types.csv b/src/pysonata/sonata/tests/examples/lgn_node_types.csv index 63ff2cf..dc2dda5 100644 --- a/src/pysonata/sonata/tests/examples/lgn_node_types.csv +++ b/src/pysonata/sonata/tests/examples/lgn_node_types.csv @@ -1,4 +1,4 @@ -node_type_id model_type model_template model_processing ei pop_name location -0 virtual NONE NONE e tON_001 LGN -1 virtual NONE NONE e tOFF_001 LGN -2 virtual NONE NONE e tONOFF_001 LGN +node_type_id model_type ei pop_name location +0 virtual e tON_001 LGN +1 virtual e tOFF_001 LGN +2 virtual e tONOFF_001 LGN diff --git a/src/pysonata/sonata/tests/examples/lgn_nodes.h5 b/src/pysonata/sonata/tests/examples/lgn_nodes.h5 index b126dd6..498f1ad 100644 Binary files a/src/pysonata/sonata/tests/examples/lgn_nodes.h5 and b/src/pysonata/sonata/tests/examples/lgn_nodes.h5 differ diff --git a/src/pysonata/sonata/tests/examples/lgn_v1_edges.h5 b/src/pysonata/sonata/tests/examples/lgn_v1_edges.h5 index ef61d7f..8902f1e 100644 Binary files a/src/pysonata/sonata/tests/examples/lgn_v1_edges.h5 and b/src/pysonata/sonata/tests/examples/lgn_v1_edges.h5 differ diff --git a/src/pysonata/sonata/tests/examples/v1_nodes.h5 b/src/pysonata/sonata/tests/examples/v1_nodes.h5 index 5850a9f..f3899b0 100644 Binary files a/src/pysonata/sonata/tests/examples/v1_nodes.h5 and b/src/pysonata/sonata/tests/examples/v1_nodes.h5 differ diff --git a/src/pysonata/sonata/tests/examples/v1_v1_edges.h5 b/src/pysonata/sonata/tests/examples/v1_v1_edges.h5 index 318b3d0..ad927bd 100644 Binary files a/src/pysonata/sonata/tests/examples/v1_v1_edges.h5 and b/src/pysonata/sonata/tests/examples/v1_v1_edges.h5 differ diff --git a/src/pysonata/sonata/tests/test_types.py b/src/pysonata/sonata/tests/test_types.py index f107998..650b2ed 100644 --- a/src/pysonata/sonata/tests/test_types.py +++ b/src/pysonata/sonata/tests/test_types.py @@ -25,7 +25,6 @@ def test_node_types(net): assert(1 in node_types) node_type1 = node_types[1] assert(node_type1['ei'] == 'e') - assert(node_type1['model_template'] is None) assert(node_type1['node_type_id'] == 1) assert(node_type1['location'] == 'LGN') @@ -63,4 +62,4 @@ def test_edge_types(net): if __name__ == '__main__': test_node_types(net()) - test_edge_types(net()) + #test_edge_types(net())