• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

BlueBrain / libsonata / 5796608470

pending completion
5796608470

Pull #285

github

jorblancoa
Remove spikes_ and build on the fly from raw vectors
Pull Request #285: Return a structured array for the spikes in python

24 of 24 new or added lines in 1 file covered. (100.0%)

1796 of 1875 relevant lines covered (95.79%)

76.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.02
/src/report_reader.cpp
1
#include <bbp/sonata/report_reader.h>
2
#include <fmt/format.h>
3

4
#include <algorithm>  // std::copy, std::find, std::lower_bound, std::upper_bound
5
#include <iterator>   // std::advance, std::next
6

7
constexpr double EPSILON = 1e-6;
8

9
H5::EnumType<bbp::sonata::SpikeReader::Population::Sorting> create_enum_sorting() {
16✔
10
    using bbp::sonata::SpikeReader;
11
    return H5::EnumType<SpikeReader::Population::Sorting>(
12
        {{"none", SpikeReader::Population::Sorting::none},
13
         {"by_id", SpikeReader::Population::Sorting::by_id},
14
         {"by_time", SpikeReader::Population::Sorting::by_time}});
64✔
15
}
16

17
HIGHFIVE_REGISTER_TYPE(bbp::sonata::SpikeReader::Population::Sorting, create_enum_sorting)
16✔
18

19
namespace {
20

21
using bbp::sonata::CompartmentID;
22
using bbp::sonata::ElementID;
23
using bbp::sonata::NodeID;
24
using bbp::sonata::Selection;
25
using bbp::sonata::Spike;
26
using bbp::sonata::Spikes;
27

28
void filterNodeIDUnsorted(Spikes& spikes, const Selection& node_ids) {
6✔
29
    const auto values = node_ids.flatten();
6✔
30
    const auto new_end =
31
        std::remove_if(spikes.begin(), spikes.end(), [&values](const Spike& spike) {
66✔
32
            return std::find(values.cbegin(), values.cend(), spike.first) == values.cend();
22✔
33
        });
6✔
34
    spikes.erase(new_end, spikes.end());
6✔
35
}
6✔
36

37
void filterNodeIDSorted(Spikes& spikes, const Selection& node_ids) {
2✔
38
    Spikes _spikes;
4✔
39
    for (const auto& range : node_ids.ranges()) {
4✔
40
        const auto begin = std::lower_bound(spikes.begin(),
41
                                            spikes.end(),
42
                                            std::make_pair(range.first, 0.),
2✔
43
                                            [](const Spike& spike1, const Spike& spike2) {
4✔
44
                                                return spike1.first < spike2.first;
4✔
45
                                            });
2✔
46
        const auto end = std::upper_bound(spikes.begin(),
47
                                          spikes.end(),
48
                                          std::make_pair(range.second - 1, 0.),
2✔
49
                                          [](const Spike& spike1, const Spike& spike2) {
6✔
50
                                              return spike1.first < spike2.first;
6✔
51
                                          });
2✔
52

53
        std::move(begin, end, std::back_inserter(_spikes));
2✔
54
        spikes.erase(begin, end);  // have to erase, because otherwise it is no more sorted
2✔
55
    }
56
    spikes = std::move(_spikes);
2✔
57
}
2✔
58

59
void filterTimestampUnsorted(Spikes& spikes, double tstart, double tstop) {
6✔
60
    auto new_end =
61
        std::remove_if(spikes.begin(), spikes.end(), [&tstart, &tstop](const Spike& spike) {
40✔
62
            return (spike.second < tstart - EPSILON) || (spike.second > tstop + EPSILON);
20✔
63
        });
6✔
64
    spikes.erase(new_end, spikes.end());
6✔
65
}
6✔
66

67
void filterTimestampSorted(Spikes& spikes, double tstart, double tstop) {
4✔
68
    const auto end = std::upper_bound(spikes.begin(),
69
                                      spikes.end(),
70
                                      std::make_pair(0UL, tstop + EPSILON),
4✔
71
                                      [](const Spike& spike1, const Spike& spike2) {
10✔
72
                                          return spike1.second < spike2.second;
10✔
73
                                      });
4✔
74
    spikes.erase(end, spikes.end());
4✔
75
    const auto begin = std::lower_bound(spikes.begin(),
76
                                        spikes.end(),
77
                                        std::make_pair(0UL, tstart - EPSILON),
4✔
78
                                        [](const Spike& spike1, const Spike& spike2) {
8✔
79
                                            return spike1.second < spike2.second;
8✔
80
                                        });
4✔
81
    spikes.erase(spikes.begin(), begin);
4✔
82
}
4✔
83

84
inline void emplace_ids(NodeID& key, NodeID node_id, ElementID /* element_id */) {
92✔
85
    key = node_id;
92✔
86
}
92✔
87

88
inline void emplace_ids(CompartmentID& key, NodeID node_id, ElementID element_id) {
70✔
89
    key[0] = node_id;
70✔
90
    key[1] = element_id;
70✔
91
}
70✔
92

93
}  // anonymous namespace
94

95
namespace bbp {
96
namespace sonata {
97

98
SpikeReader::SpikeReader(const std::string& filename)
2✔
99
    : filename_(filename) {}
2✔
100

101
std::vector<std::string> SpikeReader::getPopulationNames() const {
2✔
102
    H5::File file(filename_, H5::File::ReadOnly);
2✔
103
    return file.getGroup("/spikes").listObjectNames();
6✔
104
}
105

106
auto SpikeReader::openPopulation(const std::string& populationName) const -> const Population& {
18✔
107
    if (populations_.find(populationName) == populations_.end()) {
18✔
108
        populations_.emplace(populationName, Population{filename_, populationName});
8✔
109
    }
110

111
    return populations_.at(populationName);
18✔
112
}
113

114
std::tuple<double, double> SpikeReader::Population::getTimes() const {
2✔
115
    return std::tie(tstart_, tstop_);
4✔
116
}
117

118
Spikes SpikeReader::Population::createSpikes() const {
10✔
119
    Spikes spikes;
10✔
120
    std::transform(node_ids_.begin(),
121
                   node_ids_.end(),
122
                   timestamps_.begin(),
123
                   std::back_inserter(spikes),
124
                   [](Spike::first_type node_id, Spike::second_type timestamp) {
40✔
125
                       return std::make_pair(node_id, timestamp);
40✔
126
                   });
10✔
127
    return spikes;
10✔
128
}
129

130
Spikes SpikeReader::Population::get(const nonstd::optional<Selection>& node_ids,
10✔
131
                                    const nonstd::optional<double>& tstart,
132
                                    const nonstd::optional<double>& tstop) const {
133
    const double start = tstart.value_or(tstart_);
10✔
134
    const double stop = tstop.value_or(tstop_);
10✔
135

136
    if (start < 0 - EPSILON || stop < 0 - EPSILON) {
10✔
137
        throw SonataError("Times cannot be negative");
×
138
    }
139

140
    if (start > stop) {
10✔
141
        throw SonataError("tstart should be <= to tstop");
×
142
    }
143

144
    if (node_ids and node_ids->empty()) {
10✔
145
        return Spikes{};
×
146
    }
147

148
    auto spikes = createSpikes();
20✔
149
    filterTimestamp(spikes, start, stop);
10✔
150

151
    if (node_ids) {
10✔
152
        filterNode(spikes, node_ids.value());
8✔
153
    }
154

155
    return spikes;
10✔
156
}
157

158
SpikesArrays SpikeReader::Population::getArrays(const nonstd::optional<Selection>& node_ids,
×
159
                                                const nonstd::optional<double>& tstart,
160
                                                const nonstd::optional<double>& tstop) const {
161
    if (!node_ids && !tstart && !tstop) {
×
162
        return std::make_pair(node_ids_, timestamps_);
×
163
    }
164

165
    auto spikes = get(node_ids, tstart, tstop);
×
166

167
    std::vector<NodeID> nodeids;
×
168
    std::vector<double> timestamps;
×
169
    for (const auto& pair : spikes) {
×
170
        nodeids.push_back(pair.first);
×
171
        timestamps.push_back(pair.second);
×
172
    }
173

174
    return std::make_pair(nodeids, timestamps);
×
175
}
176

177
SpikeReader::Population::Sorting SpikeReader::Population::getSorting() const {
6✔
178
    return sorting_;
6✔
179
}
180

181
SpikeReader::Population::Population(const std::string& filename,
8✔
182
                                    const std::string& populationName) {
8✔
183
    H5::File file(filename, H5::File::ReadOnly);
16✔
184
    const auto pop_path = std::string("/spikes/") + populationName;
24✔
185
    const auto pop = file.getGroup(pop_path);
16✔
186

187
    pop.getDataSet("node_ids").read(node_ids_);
8✔
188
    pop.getDataSet("timestamps").read(timestamps_);
8✔
189

190
    if (node_ids_.size() != timestamps_.size()) {
8✔
191
        throw SonataError(
192
            "In spikes file, 'node_ids' and 'timestamps' does not have the same size.");
×
193
    }
194

195
    if (pop.hasAttribute("sorting")) {
8✔
196
        pop.getAttribute("sorting").read(sorting_);
8✔
197
    }
198

199
    if (sorting_ == Sorting::by_time) {
8✔
200
        tstart_ = timestamps_.empty() ? 0 : timestamps_.front();
2✔
201
        tstop_ = timestamps_.empty() ? 0 : timestamps_.back();
2✔
202
    } else {
203
        tstart_ = timestamps_.empty() ? 0 : *min_element(timestamps_.cbegin(), timestamps_.cend());
6✔
204
        tstop_ = timestamps_.empty() ? 0 : *max_element(timestamps_.cbegin(), timestamps_.cend());
6✔
205
    }
206
}
8✔
207

208
void SpikeReader::Population::filterNode(Spikes& spikes, const Selection& node_ids) const {
8✔
209
    if (sorting_ == Sorting::by_id) {
8✔
210
        filterNodeIDSorted(spikes, node_ids);
2✔
211
    } else {
212
        filterNodeIDUnsorted(spikes, node_ids);
6✔
213
    }
214
}
8✔
215

216
void SpikeReader::Population::filterTimestamp(Spikes& spikes, double tstart, double tstop) const {
10✔
217
    if (sorting_ == Sorting::by_time) {
10✔
218
        filterTimestampSorted(spikes, tstart, tstop);
4✔
219
    } else {
220
        filterTimestampUnsorted(spikes, tstart, tstop);
6✔
221
    }
222
}
10✔
223

224
template <typename T>
225
ReportReader<T>::ReportReader(const std::string& filename)
8✔
226
    : file_(filename, H5::File::ReadOnly) {}
8✔
227

228
template <typename T>
229
std::vector<std::string> ReportReader<T>::getPopulationNames() const {
4✔
230
    return file_.getGroup("/report").listObjectNames();
4✔
231
}
232

233
template <typename T>
234
auto ReportReader<T>::openPopulation(const std::string& populationName) const -> const Population& {
10✔
235
    if (populations_.find(populationName) == populations_.end()) {
10✔
236
        populations_.emplace(populationName, Population{file_, populationName});
10✔
237
    }
238

239
    return populations_.at(populationName);
10✔
240
}
241

242
template <typename T>
243
ReportReader<T>::Population::Population(const H5::File& file, const std::string& populationName)
10✔
244
    : pop_group_(file.getGroup(std::string("/report/") + populationName))
245
    , is_node_ids_sorted_(false) {
10✔
246
    const auto mapping_group = pop_group_.getGroup("mapping");
30✔
247
    mapping_group.getDataSet("node_ids").read(node_ids_);
10✔
248

249
    std::vector<uint64_t> index_pointers;
10✔
250
    mapping_group.getDataSet("index_pointers").read(index_pointers);
10✔
251

252
    if (index_pointers.size() != (node_ids_.size() + 1)) {
10✔
253
        throw SonataError("'index_pointers' dataset size must be 'node_ids' size plus one");
×
254
    }
255

256
    // Expand the pointers into tuples that define the range of each GID
257
    size_t element_ids_count = 0;
10✔
258
    for (size_t i = 0; i < node_ids_.size(); ++i) {
210✔
259
        node_ranges_.emplace_back(index_pointers[i], index_pointers[i + 1]);  // Range of GID
200✔
260
        node_offsets_.emplace_back(element_ids_count);                        // Offset in output
200✔
261
        node_index_.emplace_back(i);                                          // Index of previous
200✔
262

263
        element_ids_count += (index_pointers[i + 1] - index_pointers[i]);
200✔
264
    }
265
    node_offsets_.emplace_back(element_ids_count);
10✔
266

267
    {  // Sort the index according to the GIDs, if not sorted in file
268
        if (mapping_group.getDataSet("node_ids").hasAttribute("sorted")) {
10✔
269
            uint8_t sorted = 0;
4✔
270
            mapping_group.getDataSet("node_ids").getAttribute("sorted").read(sorted);
4✔
271
            is_node_ids_sorted_ = (sorted != 0);
4✔
272
        }
273

274
        if (!is_node_ids_sorted_) {
10✔
275
            // Note: The idea is to sort the positions to access the values, allowing us to
276
            //       maintain all vectors intact, while still being able to index the data
277
            std::sort(node_index_.begin(), node_index_.end(), [&](const size_t i, const size_t j) {
1,014✔
278
                return node_ids_[i] < node_ids_[j];
504✔
279
            });
280
        }
281
    }
282

283
    {  // Get times
284
        std::vector<double> times;
20✔
285
        mapping_group.getDataSet("time").read(times);
10✔
286
        tstart_ = times[0];
10✔
287
        tstop_ = times[1];
10✔
288
        tstep_ = times[2];
10✔
289
        mapping_group.getDataSet("time").getAttribute("units").read(time_units_);
10✔
290
        size_t i = 0;
10✔
291
        for (double t = tstart_; t < tstop_ - EPSILON; t += tstep_, ++i) {
150✔
292
            times_index_.emplace_back(i, t);
140✔
293
        }
294
    }
295

296
    pop_group_.getDataSet("data").getAttribute("units").read(data_units_);
10✔
297
}
10✔
298

299
template <typename T>
300
std::tuple<double, double, double> ReportReader<T>::Population::getTimes() const {
4✔
301
    return std::tie(tstart_, tstop_, tstep_);
4✔
302
}
303

304
template <typename T>
305
std::string ReportReader<T>::Population::getTimeUnits() const {
4✔
306
    return time_units_;
4✔
307
}
308

309
template <typename T>
310
std::string ReportReader<T>::Population::getDataUnits() const {
4✔
311
    return data_units_;
4✔
312
}
313

314
template <typename T>
315
bool ReportReader<T>::Population::getSorted() const {
4✔
316
    return is_node_ids_sorted_;
4✔
317
}
318

319
template <typename T>
320
std::vector<NodeID> ReportReader<T>::Population::getNodeIds() const {
4✔
321
    return node_ids_;
4✔
322
}
323

324
template <typename T>
325
typename ReportReader<T>::Population::NodeIdElementLayout
326
ReportReader<T>::Population::getNodeIdElementLayout(
28✔
327
    const nonstd::optional<Selection>& node_ids,
328
    const nonstd::optional<size_t>& _block_gap_limit) const {
329
    NodeIdElementLayout result;
28✔
330
    std::vector<NodeID> concrete_node_ids;
56✔
331
    size_t element_ids_count = 0;
28✔
332

333
    // Set the gap between IO blocks while fetching data (Default: 64MB / 4 x GPFS blocks)
334
    const size_t block_gap_limit = _block_gap_limit.value_or(16777216);
28✔
335

336
    if (block_gap_limit < 4194304) {
28✔
337
        throw SonataError("block_gap_limit must be at least 4194304 (16MB / 1 x GPFS block)");
4✔
338
    }
339

340
    // Take all nodes if no selection is provided
341
    if (!node_ids) {
24✔
342
        concrete_node_ids = node_ids_;
4✔
343
        result.node_ranges = node_ranges_;
4✔
344
        result.node_offsets = node_offsets_;
4✔
345
        result.node_index = node_index_;
4✔
346
        element_ids_count = node_offsets_.back();
4✔
347
    } else if (!node_ids->empty()) {
20✔
348
        const auto selected_node_ids = node_ids->flatten();
36✔
349

350
        for (const auto node_id : selected_node_ids) {
60✔
351
            const auto it = std::lower_bound(node_index_.begin(),
42✔
352
                                             node_index_.end(),
353
                                             node_id,
354
                                             [&](const size_t i, const NodeID node_id) {
182✔
355
                                                 return node_ids_[i] < node_id;
182✔
356
                                             });
357

358
            if (it != node_index_.end() && node_ids_[*it] == node_id) {
42✔
359
                const auto& range = node_ranges_[*it];
26✔
360

361
                concrete_node_ids.emplace_back(node_id);
26✔
362
                result.node_ranges.emplace_back(range);
26✔
363
                result.node_offsets.emplace_back(element_ids_count);
26✔
364
                result.node_index.emplace_back(result.node_index.size());
26✔
365

366
                element_ids_count += (range.second - range.first);
26✔
367
            }
368
        }
369
    } else {
370
        // node_ids Selection exists, but is empty
371
    }
372

373
    // Extract the ElementIDs from the GIDs
374
    if (!concrete_node_ids.empty()) {
24✔
375
        // Sort the index by the selected ranges
376
        std::sort(result.node_index.begin(),
18✔
377
                  result.node_index.end(),
378
                  [&](const size_t i, const size_t j) {
800✔
379
                      return result.node_ranges[i].first < result.node_ranges[j].first;
400✔
380
                  });
381

382
        // Generate the {min,max} IO blocks for the requests
383
        size_t offset = 0;
18✔
384
        for (size_t i = 0; (i + 1) < result.node_index.size(); i++) {
106✔
385
            const auto index = result.node_index[i];
88✔
386
            const auto index_next = result.node_index[i + 1];
88✔
387
            const auto max = result.node_ranges[index].second;
88✔
388
            const auto min_next = result.node_ranges[index_next].first;
88✔
389

390
            if ((min_next - max) > block_gap_limit) {
88✔
391
                result.min_max_blocks.emplace_back(std::make_pair(offset, (i + 1)));
×
392
                offset = (i + 1);
×
393
            }
394
        }
395
        result.min_max_blocks.emplace_back(std::make_pair(offset, result.node_index.size()));
18✔
396

397
        // Fill the GID-ElementID mapping in blocks to reduce the file system overhead
398
        std::vector<ElementID> element_ids;
36✔
399
        auto dataset_elem_ids = pop_group_.getGroup("mapping").getDataSet("element_ids");
36✔
400

401
        result.ids.resize(element_ids_count);
18✔
402

403
        for (const auto& min_max_block : result.min_max_blocks) {
36✔
404
            const auto first_index = result.node_index[min_max_block.first];
18✔
405
            const auto last_index = result.node_index[min_max_block.second - 1];
18✔
406
            const auto min = result.node_ranges[first_index].first;
18✔
407
            const auto max = result.node_ranges[last_index].second;
18✔
408

409
            dataset_elem_ids.select({min}, {max - min}).read(element_ids);
18✔
410

411
            // Copy the values for each of the GIDs assigned into this block
412
            for (size_t i = min_max_block.first; i < min_max_block.second; ++i) {
124✔
413
                const auto index = result.node_index[i];
106✔
414
                const auto node_id = concrete_node_ids[index];
106✔
415
                const auto range = Selection::Range(result.node_ranges[index].first - min,
106✔
416
                                                    result.node_ranges[index].second - min);
106✔
417

418
                auto offset = result.node_offsets[index];
106✔
419
                for (auto i = range.first; i < range.second; i++, offset++) {
268✔
420
                    emplace_ids(result.ids[offset], node_id, element_ids[i]);
162✔
421
                }
422
            }
423
        }
424

425
        // Temp. fix: When you ask for a large hyperslab in a dataset and then move
426
        //            to another dataset in the same file where you also ask for
427
        //            another large range, the next IOps take an extra few seconds.
428
        //            We observed that fooling HDF5 hides the issue, but we should
429
        //            verify this behaviour once new releases of HDF5 are available.
430
        const auto min_max_block = result.min_max_blocks.back();
18✔
431
        const auto index = result.node_index[min_max_block.first];
18✔
432
        dataset_elem_ids.select({result.node_ranges[index].first}, {1}).read(element_ids);
18✔
433
    }
434

435
    return result;
48✔
436
}
437

438
template <typename T>
439
std::pair<size_t, size_t> ReportReader<T>::Population::getIndex(
30✔
440
    const nonstd::optional<double>& tstart, const nonstd::optional<double>& tstop) const {
441
    std::pair<size_t, size_t> indexes;
30✔
442

443
    const double start = tstart.value_or(tstart_);
30✔
444
    const double stop = tstop.value_or(tstop_);
30✔
445

446
    if (start < 0 - EPSILON || stop < 0 - EPSILON) {
30✔
447
        throw SonataError("Times cannot be negative");
4✔
448
    }
449

450
    const auto it_start = std::find_if(times_index_.cbegin(),
26✔
451
                                       times_index_.cend(),
452
                                       [&](const std::pair<size_t, double>& v) {
102✔
453
                                           return start < v.second + EPSILON;
102✔
454
                                       });
455
    if (it_start == times_index_.end()) {
26✔
456
        throw SonataError("tstart is after the end of the range");
4✔
457
    }
458
    indexes.first = it_start->first;
22✔
459

460
    const auto it_stop =
22✔
461
        std::find_if(times_index_.crbegin(),
462
                     times_index_.crend(),
463
                     [&](const std::pair<size_t, double>& v) { return stop > v.second - EPSILON; });
186✔
464
    if (it_stop == times_index_.rend()) {
22✔
465
        throw SonataError("tstop is before the beginning of the range");
×
466
    }
467
    indexes.second = it_stop->first;
22✔
468

469
    return indexes;
22✔
470
}
471

472

473
template <typename T>
474
typename DataFrame<T>::DataType ReportReader<T>::Population::getNodeIdElementIdMapping(
12✔
475
    const nonstd::optional<Selection>& node_ids,
476
    const nonstd::optional<size_t>& block_gap_limit) const {
477
    return getNodeIdElementLayout(node_ids, block_gap_limit).ids;
12✔
478
}
479

480
template <typename T>
481
DataFrame<T> ReportReader<T>::Population::get(
30✔
482
    const nonstd::optional<Selection>& node_ids,
483
    const nonstd::optional<double>& tstart,
484
    const nonstd::optional<double>& tstop,
485
    const nonstd::optional<size_t>& tstride,
486
    const nonstd::optional<size_t>& block_gap_limit) const {
487
    size_t index_start = 0;
30✔
488
    size_t index_stop = 0;
30✔
489
    std::tie(index_start, index_stop) = getIndex(tstart, tstop);
30✔
490
    const size_t stride = tstride.value_or(1);
22✔
491
    if (stride == 0) {
22✔
492
        throw SonataError("tstride should be > 0");
2✔
493
    }
494
    if (index_start > index_stop) {
20✔
495
        throw SonataError("tstart should be <= to tstop");
4✔
496
    }
497

498
    // Retrieve the GID-ElementID layout, alongside the {min,max} blocks
499
    auto node_id_element_layout = getNodeIdElementLayout(node_ids, block_gap_limit);
32✔
500
    const auto& node_ranges = node_id_element_layout.node_ranges;
16✔
501
    const auto& node_offsets = node_id_element_layout.node_offsets;
16✔
502
    const auto& node_index = node_id_element_layout.node_index;
16✔
503
    const auto& min_max_blocks = node_id_element_layout.min_max_blocks;
16✔
504

505
    if (node_id_element_layout.ids.empty()) {  // At the end no data available (wrong node_ids?)
16✔
506
        return DataFrame<T>{{}, {}, {}};
6✔
507
    }
508

509
    // Fill times
510
    DataFrame<T> data_frame;
20✔
511
    for (size_t i = index_start; i <= index_stop; i += stride) {
64✔
512
        data_frame.times.emplace_back(times_index_[i].second);
54✔
513
    }
514

515
    // Fill ids
516
    data_frame.ids.swap(node_id_element_layout.ids);
10✔
517

518
    // Fill .data member
519
    const size_t n_time_entries = ((index_stop - index_start) / stride) + 1;
10✔
520
    const size_t element_ids_count = data_frame.ids.size();
10✔
521
    data_frame.data.resize(n_time_entries * element_ids_count);
10✔
522

523
    auto dataset = pop_group_.getDataSet("data");
30✔
524
    auto dataset_type = dataset.getDataType();
20✔
525
    if (dataset_type.getClass() != HighFive::DataTypeClass::Float || dataset_type.getSize() != 4) {
10✔
526
        throw SonataError(
527
            fmt::format("DataType of dataset 'data' should be Float32 ('{}' was found)",
528
                        dataset_type.string()));
4✔
529
    }
530

531
    std::vector<float> buffer;
16✔
532
    auto data_start = data_frame.data.begin();
8✔
533
    for (size_t timer_index = index_start; timer_index <= index_stop; timer_index += stride) {
42✔
534
        // Access the data in blocks to reduce the file system overhead
535
        for (const auto& min_max_block : min_max_blocks) {
68✔
536
            const auto first_index = node_index[min_max_block.first];
34✔
537
            const auto last_index = node_index[min_max_block.second - 1];
34✔
538
            const auto min = node_ranges[first_index].first;
34✔
539
            const auto max = node_ranges[last_index].second;
34✔
540

541
            dataset.select({timer_index, min}, {1, max - min}).read(buffer);
34✔
542

543
            // Copy the values for each of the GIDs assigned into this block
544
            const auto buffer_start = buffer.begin();
34✔
545
            for (size_t i = min_max_block.first; i < min_max_block.second; ++i) {
460✔
546
                const auto index = node_index[i];
426✔
547
                const auto range = Selection::Range(node_ranges[index].first - min,
426✔
548
                                                    node_ranges[index].second - min);
426✔
549
                const auto elements_per_gid = (range.second - range.first);
426✔
550
                const auto offset = node_offsets[index];
426✔
551

552
                // Soma report
553
                if (elements_per_gid == 1) {
426✔
554
                    data_start[offset] = buffer_start[range.first];
416✔
555
                } else {  // Elements report
556
                    std::copy(std::next(buffer_start, range.first),
20✔
557
                              std::next(buffer_start, range.second),
10✔
558
                              std::next(data_start, offset));
559
                }
560
            }
561
        }
562

563
        std::advance(data_start, element_ids_count);
34✔
564
    }
565

566
    return data_frame;
8✔
567
}
568

569
template class ReportReader<NodeID>;
570
template class ReportReader<CompartmentID>;
571

572
}  // namespace sonata
573
}  // namespace bbp
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc