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

BlueBrain / libsonata / 5785738723

pending completion
5785738723

Pull #285

github

jorblancoa
Save nodeids and timestamps arrays in case are requested as they are in the file
Pull Request #285: Return a structured array for the spikes in python

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

1794 of 1873 relevant lines covered (95.78%)

76.85 hits per line

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

93.98
/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::get(const nonstd::optional<Selection>& node_ids,
10✔
119
                                    const nonstd::optional<double>& tstart,
120
                                    const nonstd::optional<double>& tstop) const {
121
    const double start = tstart.value_or(tstart_);
10✔
122
    const double stop = tstop.value_or(tstop_);
10✔
123

124
    if (start < 0 - EPSILON || stop < 0 - EPSILON) {
10✔
125
        throw SonataError("Times cannot be negative");
×
126
    }
127

128
    if (start > stop) {
10✔
129
        throw SonataError("tstart should be <= to tstop");
×
130
    }
131

132
    if (node_ids and node_ids->empty()) {
10✔
133
        return Spikes{};
×
134
    }
135

136
    auto spikes = spikes_;
20✔
137
    filterTimestamp(spikes, start, stop);
10✔
138

139
    if (node_ids) {
10✔
140
        filterNode(spikes, node_ids.value());
8✔
141
    }
142

143
    return spikes;
10✔
144
}
145

146
SpikesArrays SpikeReader::Population::get_arrays(const nonstd::optional<Selection>& node_ids,
×
147
                                                 const nonstd::optional<double>& tstart,
148
                                                 const nonstd::optional<double>& tstop) const {
149
    if (!node_ids && !tstart && !tstop) {
×
150
        return std::make_pair(node_ids_, timestamps_);
×
151
    }
152

153
    auto spikes = get(node_ids, tstart, tstop);
×
154

155
    std::vector<NodeID> nodeids;
×
156
    std::vector<double> timestamps;
×
157
    for (const auto& pair : spikes) {
×
158
        nodeids.push_back(pair.first);
×
159
        timestamps.push_back(pair.second);
×
160
    }
161

162
    return std::make_pair(nodeids, timestamps);
×
163
}
164

165
SpikeReader::Population::Sorting SpikeReader::Population::getSorting() const {
6✔
166
    return sorting_;
6✔
167
}
168

169
SpikeReader::Population::Population(const std::string& filename,
8✔
170
                                    const std::string& populationName) {
8✔
171
    H5::File file(filename, H5::File::ReadOnly);
16✔
172
    const auto pop_path = std::string("/spikes/") + populationName;
24✔
173
    const auto pop = file.getGroup(pop_path);
16✔
174

175
    pop.getDataSet("node_ids").read(node_ids_);
8✔
176
    pop.getDataSet("timestamps").read(timestamps_);
8✔
177

178
    if (node_ids_.size() != timestamps_.size()) {
8✔
179
        throw SonataError(
180
            "In spikes file, 'node_ids' and 'timestamps' does not have the same size.");
×
181
    }
182

183
    std::transform(std::make_move_iterator(node_ids_.begin()),
184
                   std::make_move_iterator(node_ids_.end()),
185
                   std::make_move_iterator(timestamps_.begin()),
186
                   std::back_inserter(spikes_),
8✔
187
                   [](Spike::first_type&& node_id, Spike::second_type&& timestamp) {
30✔
188
                       return std::make_pair(std::move(node_id), std::move(timestamp));
30✔
189
                   });
8✔
190

191
    if (pop.hasAttribute("sorting")) {
8✔
192
        pop.getAttribute("sorting").read(sorting_);
8✔
193
    }
194

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

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

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

220
template <typename T>
221
ReportReader<T>::ReportReader(const std::string& filename)
8✔
222
    : file_(filename, H5::File::ReadOnly) {}
8✔
223

224
template <typename T>
225
std::vector<std::string> ReportReader<T>::getPopulationNames() const {
4✔
226
    return file_.getGroup("/report").listObjectNames();
4✔
227
}
228

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

235
    return populations_.at(populationName);
10✔
236
}
237

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

245
    std::vector<uint64_t> index_pointers;
10✔
246
    mapping_group.getDataSet("index_pointers").read(index_pointers);
10✔
247

248
    if (index_pointers.size() != (node_ids_.size() + 1)) {
10✔
249
        throw SonataError("'index_pointers' dataset size must be 'node_ids' size plus one");
×
250
    }
251

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

259
        element_ids_count += (index_pointers[i + 1] - index_pointers[i]);
200✔
260
    }
261
    node_offsets_.emplace_back(element_ids_count);
10✔
262

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

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

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

292
    pop_group_.getDataSet("data").getAttribute("units").read(data_units_);
10✔
293
}
10✔
294

295
template <typename T>
296
std::tuple<double, double, double> ReportReader<T>::Population::getTimes() const {
4✔
297
    return std::tie(tstart_, tstop_, tstep_);
4✔
298
}
299

300
template <typename T>
301
std::string ReportReader<T>::Population::getTimeUnits() const {
4✔
302
    return time_units_;
4✔
303
}
304

305
template <typename T>
306
std::string ReportReader<T>::Population::getDataUnits() const {
4✔
307
    return data_units_;
4✔
308
}
309

310
template <typename T>
311
bool ReportReader<T>::Population::getSorted() const {
4✔
312
    return is_node_ids_sorted_;
4✔
313
}
314

315
template <typename T>
316
std::vector<NodeID> ReportReader<T>::Population::getNodeIds() const {
4✔
317
    return node_ids_;
4✔
318
}
319

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

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

332
    if (block_gap_limit < 4194304) {
28✔
333
        throw SonataError("block_gap_limit must be at least 4194304 (16MB / 1 x GPFS block)");
4✔
334
    }
335

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

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

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

357
                concrete_node_ids.emplace_back(node_id);
26✔
358
                result.node_ranges.emplace_back(range);
26✔
359
                result.node_offsets.emplace_back(element_ids_count);
26✔
360
                result.node_index.emplace_back(result.node_index.size());
26✔
361

362
                element_ids_count += (range.second - range.first);
26✔
363
            }
364
        }
365
    } else {
366
        // node_ids Selection exists, but is empty
367
    }
368

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

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

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

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

397
        result.ids.resize(element_ids_count);
18✔
398

399
        for (const auto& min_max_block : result.min_max_blocks) {
36✔
400
            const auto first_index = result.node_index[min_max_block.first];
18✔
401
            const auto last_index = result.node_index[min_max_block.second - 1];
18✔
402
            const auto min = result.node_ranges[first_index].first;
18✔
403
            const auto max = result.node_ranges[last_index].second;
18✔
404

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

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

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

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

431
    return result;
48✔
432
}
433

434
template <typename T>
435
std::pair<size_t, size_t> ReportReader<T>::Population::getIndex(
30✔
436
    const nonstd::optional<double>& tstart, const nonstd::optional<double>& tstop) const {
437
    std::pair<size_t, size_t> indexes;
30✔
438

439
    const double start = tstart.value_or(tstart_);
30✔
440
    const double stop = tstop.value_or(tstop_);
30✔
441

442
    if (start < 0 - EPSILON || stop < 0 - EPSILON) {
30✔
443
        throw SonataError("Times cannot be negative");
4✔
444
    }
445

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

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

465
    return indexes;
22✔
466
}
467

468

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

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

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

501
    if (node_id_element_layout.ids.empty()) {  // At the end no data available (wrong node_ids?)
16✔
502
        return DataFrame<T>{{}, {}, {}};
6✔
503
    }
504

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

511
    // Fill ids
512
    data_frame.ids.swap(node_id_element_layout.ids);
10✔
513

514
    // Fill .data member
515
    const size_t n_time_entries = ((index_stop - index_start) / stride) + 1;
10✔
516
    const size_t element_ids_count = data_frame.ids.size();
10✔
517
    data_frame.data.resize(n_time_entries * element_ids_count);
10✔
518

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

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

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

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

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

559
        std::advance(data_start, element_ids_count);
34✔
560
    }
561

562
    return data_frame;
8✔
563
}
564

565
template class ReportReader<NodeID>;
566
template class ReportReader<CompartmentID>;
567

568
}  // namespace sonata
569
}  // 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