Running multiple standalone simulations [Request for Feedback]

Unfortunately, this is not possible at the moment in any clean kind of way. This is one of the things that the store/restore method deals with (Running a simulation — Brian 2 2.5.4 documentation) which is not available for C++ standalone.

But if your issue is only the memory usage of the monitors, then I think an easier solution would be to use C++ code to write your recordings directly to disk instead of keeping them in memory. For a SpikeMonitor, you can find a solution here: Real Time Monitor - #2 by mstimberg
For a StateMonitor, you can use a function like this:

def disk_writer(filename, size, name):
    code = '''
    double %name%(int index, double value) {
        static std::ofstream outfile("%filename%", ios::binary | ios::out);
        static double data_container[%size%];  // stores data for one timestep
        data_container[index] = value;
        if (index == %size% - 1) { //write to disk when last value has been written
            outfile.write(reinterpret_cast<char*>(data_container), %size%*sizeof(double));
        }
        return 0.0;
    }
    '''.replace('%name%', name).replace('%filename%', filename).replace('%size%', str(size))

    @implementation('cpp', code)
    @check_units(index=1, value=1, result=1)
    def disk_writer(index, value):
        raise NotImplementedError('C++ only')
    return disk_writer

This can then be used in a run_regularly operation to replace a StateMonitor:

store_v = disk_writer(file_name, len(group), 'store_v')

group.run_regularly('dummy = store_v(i, v)')

After the run, you can reload the data like this:

v_values = np.fromfile(file_name)
v_values = v_values.reshape((-1, len(group))).T

Here’s a full example demonstrating that it records the same thing as the StateMonitor:

Full code example
import os
from brian2 import *
set_device('cpp_standalone')
group = NeuronGroup(2, 'dv/dt = -v/(10*ms) + 0.1/sqrt(5*ms)*xi : 1', method='euler')
mon = StateMonitor(group, 'v', record=True)

def disk_writer(filename, size, name):
    code = '''
    double %name%(int index, double value) {
        static std::ofstream outfile("%filename%", ios::binary | ios::out);
        static double data_container[%size%];  // stores data for one timestep
        data_container[index] = value;
        if (index == %size% - 1) { //write to disk when last value has been written
            outfile.write(reinterpret_cast<char*>(data_container), %size%*sizeof(double));
        }
        return 0.0;
    }
    '''.replace('%name%', name).replace('%filename%', filename).replace('%size%', str(size))

    @implementation('cpp', code)
    @check_units(index=1, value=1, result=1)
    def disk_writer(index, value):
        raise NotImplementedError('C++ only')
    return disk_writer

file_name = '/tmp/v_values.npy'
store_v = disk_writer(file_name, len(group), 'store_v')

group.run_regularly('dummy = store_v(i, v)')

run(100*ms, report='text')

v_values = np.fromfile(file_name)
v_values = v_values.reshape((-1, len(group))).T
fig, axs = plt.subplots(2, 1, sharex=True, sharey=True)
axs[0].plot(mon.t/ms, mon.v.T)
axs[1].plot(mon.t/ms, v_values.T)
plt.show()

Figure_1

1 Like