Extended input-output optionsยถ
Neural structures are fascinating and very interesting to observe even in isolation, but they are most useful when there is a way to externally influence them and observe the resulting differences in behaviour. To cover this need, NeuroML offers an extensive set of <input>s that can be modelled and applied on a <network>, and the <OutputFile> specification format to record and observe the simulated quantities.
However, the scientifically interesting set of input sources can often exceed what NeuroML can model through its dynamical equations (for example, experimental captures of real phenomena), and the <OutputFile> specification is also impractical for large-scale recording and real-time processing. Furthermore, experimental setups may include some sort of external environment (be it another brain region, organ or the organismโs surroundings) that the simulated model interacts with. There are
cases where the full system and interaction can be captured inside a NeuroML model (see the Pong example) but again all kinds of interaction cannot be expressed within NeuroML plus LEMS.
This article introduces EDENโs extended capabilities for getting data in and out of the simulation, beyond what NeuroML already offers.
The descriptions and usage examples for each of EDENโs extended input/output capabilities follows:
Extended output:
Extended input:
General conceptsยถ
EDEN receives information from and provides information to other parts of the computer itโs running on, in the form of streams. These streams are sequences of bits of information (of possibly unknown length), that flow in strict (simulation) time order. Different types of transmitted information may always be cast to streams (for example, independent packets can be serialised into time series, with information imputed for the lost packets as appropriate). This is done with auxiliary programming thatโs specific to each experimental apparatus, and is thus not part of EDEN but part of the experiment.
These streams of information within EDEN may take one of two forms: they can be either:
Time series, which are observed as quantities changing values over time (for example, an electrical potential or dopamine eligibility trace); or
Event series which are instances of an event that occur at specific points in time (for example, action potentials from outside the simulated network, other instantaneous signals).
EDENโs extended I/O tags commonly use two attributes, href and format to specify where (in computer access space) and how (in form of expression) to exchange data. Due to the technologies involved, these two parameters are not entirely independent. The values of these attributes are as explained below:
Data URLโsยถ
The URI scheme (the <scheme>:// part of a URL) determines how an information resource should be found and accessed (common examples are http, tel and zoomโข). The rest of the URL that follows is decoded and retrieved following that scheme, so we have to specify it first.
The available options for source/destination URIโs and their meanings are:
no scheme: A regular file, with a path relative to (i.e. starts from the same point as) the file the URL is written on. The meaning is the same as the
fileNamein classic NeuroML.file://: A regular file, with a path relative to the current working directory (e.g. the notebook thatโs being used, the folder open when clicking on a program, or what the command line starts with, tryos.getcwd()if in doubt).
The URI schemes tcp://, pipe://(named pipes for Windows), unix://(Unix domain pipes), and mpi:// are planned to be supported soon.
Data formatยถ
The options for the format of data transmitted to/from the specified URL are:
ascii_v0: The first text-based format type, that closely resembles the de facto NeuroML output format. Itโs human-readable text files with each sample of the recording on a separate line, that (typically) starts with the simulated time that the sample refers to.
More data formats may be introduced as needed.
Extended outputยถ
Time series with EdenOutputFileยถ
In NeuroML, the evolution over time of quantities in the simulated <network> can be saved to files and retrieved thanks to <OutputFile>s in the <Simulation>. Each <OutputFile> writes down a time series of one or more physical quantitys specified by the <OutputColumn>s, throughout the simulation.
A problem that arises is that <OutputFile> records the quantitiesโ values for every single timestep of the simulation, which very quickly makes it impractical to record many time series over a long simulation duration - even if only few of the points in time that are recorded are actually needed. Another minor issue (which nonetheless adds unnecessary friction) is that quantities are always recorded in SI units.
To address these limitations, EDEN offers an alternative form of <OutputFile> fittingly called <EdenOutputFile>. Along with the general href and format mentioned above, this tag also accepts more parameters to get a nicer looking output.
When using this tag, each <OutputColumn> that records a dimensioned quantity must also specify the output_units to record the quantity in. This helps with ambiguity and can also save some unit conversion when the experimental pipeline uses non-SI units already.
More importantly, one can specify when to record using this tag. There are two ways to specify the sampling points in time:
either periodically over an interval,
or at some explicitly specified points in time.
The first way involves specifying the following attributes on the EdenOutputfile tag:
starting_from(default: \(0\)): The point in (simulation) time to start recording from.up_to_excluding(default: \(\infty\) ): The point in time when recording ends.sampling_interval(default: simulation step) The sampling period (interval between samples).
As an alternative to specifying a recording interval and sampling period (or going ahead with the default values), one may instead add the child tag <SamplePoints>. This tag must contain one or more tags in the form <s t="time to record">, and the time values must be in strictly increasing order.
Example: Reducing the size of simulation outputยถ
Letโs take the first single-neuron model from Introduction and record it as usual:
%%writefile SingleCell_Model.nml
<neuroml>
<iafCell id="MyFirstCellType" C="200 pF" leakConductance="10 nS" leakReversal="-70 mV" reset="-70mV" thresh="-50mV" />
<pulseGenerator id="MyFirstInputSource" delay="100ms" duration="500ms" amplitude="0.21nA"/>
<network id="MyNetwork" type="networkWithTemperature" temperature="37degC" >
<population id="Pop" component="MyFirstCellType" size="1"/>
<inputList id="MyFirstInputList" population="Pop" component="MyFirstInputSource">
<input id="0" target="Pop[0]" destination="synapses"/>
</inputList>
</network>
</neuroml>
Writing SingleCell_Model.nml
%%writefile LEMS_RecordLots_Sim.xml
<?xml version="1.0" encoding ="UTF-8" ?>
<Lems>
<include file="SingleCell_Model.nml" />
<Simulation id="MySim" length="1 s" step="10 us" target="MyNetwork" >
<OutputFile id="MyFirstOutputFile" fileName="results_more.gen.txt">
<OutputColumn id="my_first_vm" quantity="Pop[0]/v"/>
</OutputFile>
</Simulation>
<Target component="MySim"/>
</Lems>
Writing LEMS_RecordLots_Sim.xml
from eden_simulator import runEden; from matplotlib import pyplot as plt
def plot_single_cell(results, units='V'):
plt.figure(figsize=(8,4))
plt.plot(results['t'],results['Pop[0]/v'])
plt.xlabel('Time (sec)'); plt.ylabel('Membrane voltage');
results = runEden('LEMS_RecordLots_Sim.xml')
plot_single_cell(results)
Now, letโs use <EdenOutputFile> to record on just every millisecond:
%%writefile LEMS_RecordLess_Sim.xml
<?xml version="1.0" encoding ="UTF-8" ?>
<Lems>
<include file="SingleCell_Model.nml" />
<Simulation id="MySim" length="1 s" step="10 us" target="MyNetwork" >
<!-- Add an EdenOutputFile โ -->
<EdenOutputFile id="MyOutputFile" href="results_less.gen.txt" format="ascii_v0" sampling_interval="1 msec">
<OutputColumn id="my_first_vm" quantity="Pop[0]/v" output_units="mV"/> <!-- With units โ -->
</EdenOutputFile>
</Simulation>
<Target component="MySim"/>
</Lems>
Writing LEMS_RecordLess_Sim.xml
results = runEden('LEMS_RecordLess_Sim.xml')
plot_single_cell(results, units='mV')
And compare the two recordings in size:
import os; from si_prefix import si_format # or humanize or whatever
for x in [ 'results_more.gen.txt', 'results_less.gen.txt' ]:
print(f'{x}: {si_format(os.path.getsize(x))}bytes')
results_more.gen.txt: 3.4 Mbytes
results_less.gen.txt: 34.0 kbytes
And naturally, this difference is multiplied by the number of quantities that are being recorded.
Example: Recording a specific range in timeยถ
Another use case is when someone is not that interested in the whole course of the simulation, but rather on the behaviour that the network converges to (typically near the end of the simulation). In that case, the time range to record over can be specified with starting_from and up_to_excluding.
Note: This is not recommended to do while the model is still being developed. When one is still not sure about how the network starts off, there is a concern it might have settled to a different (and possibly unrealistic!) state than whatโs expected. But when thereโs no such concern any more and only the final phase needs to be recorded, it can also help save space further.
Hereโs how to record a specific range then, on the above model:
%%writefile LEMS_RecordRange_Sim.xml
<?xml version="1.0" encoding ="UTF-8" ?>
<Lems> <include file="SingleCell_Model.nml" />
<Simulation id="MySim" length="1 s" step="10 us" target="MyNetwork" >
<!-- Add an EdenOutputFile with a specific range โ -->
<EdenOutputFile id="MyOutputFile" href="results_less.gen.txt" format="ascii_v0"
starting_from="0.5 s" up_to_excluding="0.6 sec" sampling_interval="1 msec">
<OutputColumn id="my_first_vm" quantity="Pop[0]/v" output_units="mV"/>
</EdenOutputFile>
</Simulation>
<Target component="MySim"/> </Lems>
Writing LEMS_RecordRange_Sim.xml
results = runEden('LEMS_RecordRange_Sim.xml')
plot_single_cell(results, units='mV')
Event series with EdenEventOutputFileยถ
An alternative that NeuroML offers to recording the trajectories of quantities with <OutputFile> is to record discrete events that occur (in practice thatโs basically spikes from neurons, to capture the activity raster). This is done with <EventOutputfile> tags in the <Simulation>. Each <EventOutputFile> writes down a sequence of when and which spikes occurred, so that each occurrence appears as a text line with the time reference and spike source number. Different spike sources
are listed by <EventSelection>s inside the tag, and identified through their numerical idโs.
Although clear and concise, this format has some limitations when the outputs are not just ordinary files, but actually continuous streams that get fed to a post-processing pipeline (and possibly fed back to the simulation through EDENโs input extensions):
When reading a stream line by line, it is not clear whether this is the last spike to occur on the timebase, or more spikes on the same timebase follow. If the stream recipient waits to receive more information, they may have to wait until whenever the next spike appears, and if it doesnโt wait and proceeds with processing up to the time base, it may miss more events that happened on the same timestep.
In the same way, it is not known when the next event follows, and there can be any distance between events. This can be a problem for the spike stream recipient when there are real-time constraints or feedback with the simulator, as post-processing cannot know up to how far it can process the timebase.
To address these limitations, EDEN offers an alternative form of <EventOutputFile> fittingly called <EdenEventOutputFile>. Along with the general href and format mentioned above, this tag also accepts more attributes to control output:
starting_from(default: \(0\) ): The point in (simulation) time to start recording from.up_to_excluding(default: \(\infty\) ): The point in time when recording ends.maximum_interval(default: \(\infty\) ): The maximum time interval between lines.
Note that the format parameter is also used in NeuroML, with allowed values ID_TIME or TIME_ID. For this tag, the format can only be ascii_v0 (more similar to TIME_ID).
The ascii_v0 format expresses the event stream as a sequence of lines, with the difference that all spikes that happened on the same timestep are listed on a single line, and extra lines with no events may be inserted when maximum_interval requires it. Each line is therefore in this format:
<simulation time> <whitespace-separated list of zero or more ID's>
Example: Adding โheartbeatsโ to an event streamยถ
Letโs take the first single-neuron model from the previous example and record its spikes, but also add a maximum_interval this time:
%%writefile LEMS_RecordSpikes_Sim.xml
<?xml version="1.0" encoding ="UTF-8" ?>
<Lems> <include file="SingleCell_Model.nml" />
<Simulation id="MySim" length="1 s" step="10 us" target="MyNetwork" >
<!-- Add an EdenOutputFile with a specific range โ -->
<EventOutputFile id="SpikesOut" fileName="spikes_out.gen.txt" format="TIME_ID">
<EventSelection id="0" select="Pop[0]" eventPort="spike"/>
</EventOutputFile>
<EdenEventOutputFile id="DeLuxe" href="spikes_out_deluxe.gen.txt" format="ascii_v0"
maximum_interval="100 ms">
<EventSelection id="0" select="Pop[0]" eventPort="spike"/>
</EdenEventOutputFile>
</Simulation>
<Target component="MySim"/> </Lems>
Writing LEMS_RecordSpikes_Sim.xml
runEden('LEMS_RecordSpikes_Sim.xml')
with open('spikes_out.gen.txt') as f: lines_before = list(f.readlines())
with open('spikes_out_deluxe.gen.txt') as f: lines_after = list(f.readlines())
from difflib import HtmlDiff; from IPython.display import display, HTML
mydiff = HtmlDiff(tabsize=4)
display(HTML(mydiff.make_file(lines_before, lines_after, fromdesc='EventOutputFile', todesc='EdenEventOutputFile')))
# see also: !diff --side-by-side spikes_out.gen.txt spikes_out_deluxe.gen.txt
| EventOutputFile | EdenEventOutputFile | ||||
|---|---|---|---|---|---|
| n | n | 1 | ย ย ย ย ย ย ย ย ย ย ย ย ย ย +0 | ||
| 2 | ย ย ย ย ย ย ย ย ย ย ย ย +0.1 | ||||
| 1 | ย ย ย ย ย ย ย ย +0.16089ย 0 | 3 | ย ย ย ย ย ย ย ย +0.16089ย 0 | ||
| 2 | ย ย ย ย ย ย ย ย +0.22178ย 0 | 4 | ย ย ย ย ย ย ย ย +0.22178ย 0 | ||
| 3 | ย ย ย ย ย ย ย ย +0.28267ย 0 | 5 | ย ย ย ย ย ย ย ย +0.28267ย 0 | ||
| 4 | ย ย ย ย ย ย ย ย +0.34356ย 0 | 6 | ย ย ย ย ย ย ย ย +0.34356ย 0 | ||
| 5 | ย ย ย ย ย ย ย ย +0.40445ย 0 | 7 | ย ย ย ย ย ย ย ย +0.40445ย 0 | ||
| 6 | ย ย ย ย ย ย ย ย +0.46534ย 0 | 8 | ย ย ย ย ย ย ย ย +0.46534ย 0 | ||
| 7 | ย ย ย ย ย ย ย ย +0.52623ย 0 | 9 | ย ย ย ย ย ย ย ย +0.52623ย 0 | ||
| 8 | ย ย ย ย ย ย ย ย +0.58712ย 0 | 10 | ย ย ย ย ย ย ย ย +0.58712ย 0 | ||
| t | t | 11 | ย ย ย ย ย ย ย ย +0.68712 | ||
| 12 | ย ย ย ย ย ย ย ย +0.78712 | ||||
| 13 | ย ย ย ย ย ย ย ย +0.88712 | ||||
| 14 | ย ย ย ย ย ย ย ย +0.98712 | ||||
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
These timestamp-only lines may look superfluous, but they may be necessary to go forward in processing pipelines as explained above. Arguably, one could also add a regular <๐๐๐๐๐๐ถ๐๐๐๐๐๐๐๐> to that effect. But only EdenEventOutputFile can pack events the by timestep, which is also very important.
Exercise: Play with the starting_from and up_to_excluding attributes and see their effect.
Exercise: Set up a sizeable network thatโs buzzing with activity and watch how each EventOutputFile handles spikes on the same timestep.
Extended inputยถ
EDEN is able to provide a simulation with information streams coming from outside it. This greatly extends the modelling possibilities, since:
input signals can be created by any method and data pipeline, beyond what can be expressed with differential equations;
the SNN model can be plugged into a closed loop experiment, and receive feedback from processes working in a completely different way.
The external input streams to add to a simulation are specified through the <EdenTimeSeriesReader> and <EdenEventSetReader> tags. Unlike the OutputWriters, these are located inside the <network> since they effectively are part of it alongside the neurons and synapses.
The form that the information streams take is similar to that of point neurons, in that they can have one or more scalar Exposures and <OutputPort>s. This is intentional, as they are meant to interact with parts of the model as if they are regular (albeit read-only) objects of the model.
Time series with EdenTimeSeriesReaderยถ
A <EdenTimeSeriesReader> is a source of one or more time-evolving quantities, whose values are retrieved from a URL. These quantities are presented to the model as a collection of elements, where each element is uniformly structured as a collection of named scalars. One can think of elements as pseudo-point-neurons, and of their named scalars as LEMS <Exposure>s.
Along with the general href and format mentioned above, this tag also has the attribute instances which represents the number of identical elements that make up the <EdenTimeSeriesReader>. Inside the tag, <InputColumn> tags list the different named scalars, with attributes:
id: The name of the scalar quantity.dimension: The dimensionality of the quantity,nonefor dimensionless as usual for LEMS.units(if dimensional): The units for the numerical values of the quantity.
Since input streams are an extension to NeuroML (and they offer many more ways of interaction than, say, <gapJunction>s allow), they cannot be interacted with through core NeuroML. Instead, mechanisms can rely on these quantities to adjust their behaviour through <๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐>s, which is another extension that EDEN adds to NeuroML. What this means is that if a mechanismโs dynamics depend on an external quantity, then this quantity is specified as a
<๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐> (instead of a local <Requirement>) within the LEMS-specified mechanism.
The ascii_v0 data format to supply <EdenTimeSeriesReader>s with is similar to what <OutputWriter> already generates:
There is one line for each sample point in time, followed by all the mentioned time series. Sample points are laid out in strict ascending order. A difference is that the time points for samples donโt have to be regularly spaced (just like with
<EdenOutputFile>โsSamplePoints): between sample points, the quantities will retain the values given from the last sample (they will be piecewise constant over time).The quantity values for each element are laid along the line in element order. When there is more than one
<InputColumn>per element, the new value for each of its named scalars is laid out in the order theInputColumns were declared. Hence there areelements * scalarsquantity values per line (plus the timestamp at the start of the line) and the value order is element-major, scalar-minor.
Example: An arbitrary-pulse current probeยถ
<EdenTimeSeriesReader> with is a current probe of a user-specified waveform.
%%writefile ReadTimeSeries.nml
<neuroml>
<!-- A simple yet versatile current source โ -->
<ComponentType name="RefCurrentSource" extends="basePointCurrent"
description="Generates instantaneous current sourced from a VariableReference.">
<!-- Here comes the VariableRequirement โ -->
<VariableRequirement name="iPointed" dimension="current" />
<Dynamics>
<DerivedVariable name="i" dimension="current" exposure="i" value="iPointed"/>
</Dynamics>
</ComponentType>
<iafCell id="MyFirstCellType" C="200 pF" leakConductance="10 nS" leakReversal="-70 mV" reset="-70mV" thresh="-50mV" />
<RefCurrentSource id="MyRefInputSource"/> <!-- We'll have to set up a VariableRequirement for all instances โ -->
<network id="MyNetwork" type="networkWithTemperature" temperature="37degC" >
<population id="Pop" component="MyFirstCellType" size="1"/>
<inputList id="MyInputList" population="Pop" component="MyRefInputSource">
<input id="0" target="Pop[0]" destination="synapses"/>
</inputList>
<!-- Add an input stream โ -->
<EdenTimeSeriesReader id="MyFirstInputStream" href="file://timeseries.gen.txt" format="ascii_v0" instances="1" >
<InputColumn id="i" dimension="current" units="nA"/>
</EdenTimeSeriesReader>
</network>
<Simulation id="MySim" length="1 s" step="10 us" target="MyNetwork" >
<EdenOutputFile id="MyOutputFile" href="results_less.gen.txt" format="ascii_v0" sampling_interval="1 msec">
<OutputColumn id="my_first_vm" quantity="Pop[0]/v" output_units="mV"/>
</EdenOutputFile>
<!-- Add EdenCustomSetup to set up the VariableReferences โ -->
<EdenCustomSetup filename="ReadTimeSeries_CustomSetup.gen.txt"/>
</Simulation>
<Target component="MySim"/>
</neuroml>
Writing ReadTimeSeries.nml
%%writefile ReadTimeSeries_CustomSetup.gen.txt
set input MyInputList all iPointed MyFirstInputStream[0]/i # add more inputs and see what happens :)
Writing ReadTimeSeries_CustomSetup.gen.txt
Then, cook up a signal to pass to the simulation and save it in the ascii_v0 format:
import numpy as np;
N_samples = 1000; signal_dt_sec = 0.001;
times = np.array(range(N_samples)) * signal_dt_sec
# and then add stuff
signal = 0*times
signal[150:526] = 0.3 * np.sin( (times[150:526]- times[150]) / 0.04 )
signal[650:850] = 0.5 * ( (times[650:850]- times[650]) / 0.2 ) ** 2
# and write it down
with open('timeseries.gen.txt', 'wt') as f:
for (t,i) in zip(times,signal):
f.write(f'{t} {i}\n')
And run it all together:
results = runEden('ReadTimeSeries.nml')
def halfaxes(ax, color, label):
ax.set_ylabel(label, color=color)
ax.tick_params(axis='y', labelcolor=color)
plot_single_cell(results); halfaxes(plt.gca(), 'tab:blue', 'Membrane voltage (mV)')
ax2 = plt.gca().twinx()
ax2.plot(times, signal, color='tab:orange'); halfaxes(ax2, color='tab:orange', label='Current (nA)')
Exercise: Try writing a (short) timeseries.gen.txt by hand, or set up your favourite waveform and see what happens to the cell.
Event series with EdenEventSetReaderยถ
An <EdenEventSetReader> is a set of one or more spike sources, whose occurrences are retrieved from a URL. The spike sources are presented to the model as a collection of elements, where each element is uniformly structured as a collection of named ports. One can think of elements as pseudo-point-neurons, and of their named ports as LEMS <EventPort>s.
Along with the general href and format mentioned above, this tag also has the attribute instances which represents the number of identical elements that make up the <EdenTimeSeriesReader>. Inside the tag, <Port> tags list the different named ports, with no specific attributes.
Even though they are an extension to NeuroML, since the elements resemble neurons with one or more spike emitting sites, they can appear to the model as pseudo-neuron populations (just like ordinary spike sources are) and be directly used in <projection>s as pre-synaptic sites!
When connecting them to post-synapses on real neurons with a
<projection>, the interpretation of the usual<connection>parameterspreCellIdandpreSegmentchanges:preCellIdwill stand for the numbered element of theEventSet;preSegmentwill stand for the named port of said element. If unspecified when there are multiple ports, itโs assumed to be the typical portspike;preFractionAlongis not applicable, as is the case with point neurons.
Of course there may be no synaptic component on their side; hence they can only be the
presynapticPopulationin a simple, AP based<projection>.
The ascii_v0 data format to supply <EdenEventSetReader>s with is similar to what <EdenEventOutputFile> already generates:
There is one line for each distinct sample point in time, followed by all the event identifiers that occur on it. Sample points are laid out in strict ascending order.
- A difference with
<EdenEventOutputFile>is how the multiple ports of elements are identified, when present: instead of a simple integer (that identifies the elements, when each has only one port) there are two integers separated by a!character.In this case:the first number is that of the element in the event set;
the second number is that of the event
<Port>, in the order they were listed;both are needed to determine which port of which element emitted an event.
Do note, however, that
<(Eden)EventOutputFile>with its simple list of<EventSelection>scanโt write streams in this โmulti-portโ format.
Example: An arbitrary-train multi-port spike sourceยถ
A simple example to show how <EdenEventSetReader> works is with a multi-port event stream that projects to different neurons.
First, letโs see how to set up stuff model-wise:
%%writefile ReadEventSet.nml
<neuroml>
<iafCell id="MyFirstCellType" C="200 pF" leakConductance="10 nS" leakReversal="-70 mV" reset="-70mV" thresh="-50mV" />
<expOneSynapse id="MySyn" gbase="11nS" erev="0V" tauDecay="5ms"/>
<network id="MyNetwork" type="networkWithTemperature" temperature="37degC" >
<!-- Add a population with two cells -->
<population id="Pop" component="MyFirstCellType" size="2"/>
<!-- Add an event stream โ -->
<EdenEventSetReader id="MyFirstEventStream" href="eventset.gen.txt" format="ascii_v0" instances="2">
<Port id="zero"/>
<Port id="one"/>
</EdenEventSetReader>
<!-- Apply a synaptic projection from the event stream to the cells โ -->
<projection id="MyFirstStreamProjection" presynapticPopulation="MyFirstEventStream" postsynapticPopulation="Pop" synapse="MySyn">
<connectionWD id="0" preCellId="0" preSegmentId="zero" postCellId="0" weight="1" delay="10 ms"/>
<connectionWD id="1" preCellId="0" preSegmentId="one" postCellId="1" weight="1" delay="10 ms"/>
<connectionWD id="2" preCellId="1" preSegmentId="zero" postCellId="1" weight="1" delay="10 ms"/>
<connectionWD id="3" preCellId="1" preSegmentId="one" postCellId="0" weight="1" delay="10 ms"/>
</projection>
</network>
<Simulation id="MySim" length="1 s" step="100 us" target="MyNetwork" >
<OutputFile id="MyFirstOutputFile" fileName="results.gen.txt">
<OutputColumn id="my_first_vm" quantity="Pop[0]/v"/>
<OutputColumn id="my_second_vm" quantity="Pop[1]/v"/>
</OutputFile>
</Simulation>
<Target component="MySim"/>
</neuroml>
Writing ReadEventSet.nml
And then write up some โeventsโ:
%%writefile eventset.gen.txt
0.1 0!0
0.3 0!1 1!0 1!1
0.4 1!0 1!1
0.6 0!1
Writing eventset.gen.txt
And run the model plus injected spikes:
results = runEden('ReadEventSet.nml')
plt.figure(figsize=(8,4))
plt.plot(results['t'],results['Pop[0]/v'], label='My first cell')
plt.plot(results['t'],results['Pop[1]/v'], label='My second cell')
plt.xlabel('Time (sec)'); plt.ylabel('Membrane voltage (V)'); plt.legend();
Notice the delay between when the spikes happen and when the synapses are activated, just as <connectionWD> specifies.
Exercise: Repeat the example, with elements of a single port with this time, and hence simple integers as spike identifiers. In that case, the format is the same as what <EdenEventOutputFile> outputs.
In this simple case (of just a few pre-calculated spikes) one could have simply used the classic NeuroML <๐๐๐๐๐๐ฐ๐๐๐๐ข>s, but this gets unwieldy with population-wide spike trains and long durations. And, of course, this precludes dynamic live-streaming from another part of the experimental rig.
On shorting readers and writersยถ
Note that directly recording data that comes in from readers is not officially supported, because of the instantaneous input-to-output requirement they imply in conjunction with the problematic cases that can occur when running on a MPI cluster if input and output streams are distributed among cluster nodes in arbitrary ways. It is permitted without support, but there may be a delay of one timestep between the information fed to the <Eden...Reader>s and that emitted by an
(Event)OutputWriter that samples these input streams.