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

BlueBrain / MorphIO / 5679884058

pending completion
5679884058

push

github

mgeplf
cleanup

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

1968 of 2587 relevant lines covered (76.07%)

905.27 hits per line

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

81.22
/src/mut/morphology.cpp
1
#include <algorithm>  // std::find_if
2
#include <cassert>
3
#include <cctype>    // std::tolower
4
#include <iterator>  // std::back_inserter
5
#include <sstream>
6
#include <string>
7

8
#include <morphio/endoplasmic_reticulum.h>
9
#include <morphio/mitochondria.h>
10
#include <morphio/mut/endoplasmic_reticulum.h>
11
#include <morphio/mut/morphology.h>
12
#include <morphio/mut/section.h>
13
#include <morphio/mut/writers.h>
14
#include <morphio/soma.h>
15

16
#include "../shared_utils.hpp"
17

18
namespace {
19
using SectionP = std::shared_ptr<morphio::mut::Section>;
20

21
void insertSectionsBeforeSection(std::vector<SectionP>& sections_to_update,
770✔
22
                                 const std::vector<SectionP>& sections,
23
                                 const SectionP& target_section) {
24
    auto is_equal = [&target_section](const SectionP& section) {
2,734✔
25
        return (section->id() == target_section->id()) && section->hasSameShape(*target_section);
982✔
26
    };
770✔
27

28
    const auto it = std::find_if(sections_to_update.begin(), sections_to_update.end(), is_equal);
770✔
29

30
    // Note that the section with section_id is not removed at this step
31
    sections_to_update.insert(it, sections.begin(), sections.end());
770✔
32
}
770✔
33

34
void appendProperties(morphio::Property::PointLevel& to,
662✔
35
                      const morphio::Property::PointLevel& from,
36
                      int offset = 0) {
37
    morphio::_appendVector(to._points, from._points, offset);
662✔
38
    morphio::_appendVector(to._diameters, from._diameters, offset);
662✔
39

40
    if (!from._perimeters.empty()) {
662✔
41
        morphio::_appendVector(to._perimeters, from._perimeters, offset);
2✔
42
    }
43
}
662✔
44
}  // namespace
45

46

47
namespace morphio {
48
namespace mut {
49

50
using morphio::readers::ErrorMessages;
51
Morphology::Morphology(const std::string& uri, unsigned int options)
56✔
52
    : Morphology(morphio::Morphology(uri, options)) {}
56✔
53

54
Morphology::Morphology(const HighFive::Group& group, unsigned int options)
10✔
55
    : Morphology(morphio::Morphology(group, options)) {}
10✔
56

57
Morphology::Morphology(const morphio::mut::Morphology& morphology, unsigned int options)
×
58
    : _soma(std::make_shared<Soma>(*morphology.soma()))
×
59
    , _cellProperties(std::make_shared<morphio::Property::CellLevel>(*morphology._cellProperties))
×
60
    , _endoplasmicReticulum(morphology.endoplasmicReticulum())
61
    , _dendriticSpineLevel(morphology._dendriticSpineLevel) {
×
62
    for (const std::shared_ptr<Section>& root : morphology.rootSections()) {
×
63
        appendRootSection(root, true);
×
64
    }
65

66
    for (const std::shared_ptr<MitoSection>& root : morphology.mitochondria().rootSections()) {
×
67
        mitochondria().appendRootSection(root, true);
×
68
    }
69

70
    applyModifiers(options);
×
71
}
×
72

73
Morphology::Morphology(const morphio::Morphology& morphology, unsigned int options)
70✔
74
    : _soma(std::make_shared<Soma>(morphology.soma()))
70✔
75
    , _cellProperties(
76
          std::make_shared<morphio::Property::CellLevel>(morphology.properties_->_cellLevel))
70✔
77
    , _endoplasmicReticulum(morphology.endoplasmicReticulum())
70✔
78
    , _dendriticSpineLevel(morphology.properties_->_dendriticSpineLevel) {
210✔
79
    for (const morphio::Section& root : morphology.rootSections()) {
174✔
80
        appendRootSection(root, true);
104✔
81
    }
82

83
    for (const morphio::MitoSection& root : morphology.mitochondria().rootSections()) {
114✔
84
        mitochondria().appendRootSection(root, true);
44✔
85
    }
86

87
    applyModifiers(options);
70✔
88
}
70✔
89

90
/**
91
   Return false if there is no duplicate point
92
 **/
93
bool _checkDuplicatePoint(const SectionP& parent, const SectionP& current) {
476✔
94
    // Weird edge case where parent is empty: skipping it
95
    if (parent->points().empty()) {
476✔
96
        return true;
×
97
    } else if (current->points().empty()) {
476✔
98
        return false;
×
99
    } else if (parent->points().back() != current->points().front()) {
476✔
100
        return false;
8✔
101
    }
102

103
    // // As perimeter is optional, it must either be defined for parent and
104
    // current
105
    // // or not be defined at all
106
    // if(parent->perimeters().empty() != current->perimeters().empty())
107
    //     return false;
108

109
    // if(!parent->perimeters().empty() &&
110
    //    parent->perimeters()[parent->perimeters().size()-1] !=
111
    //    current->perimeters()[0])
112
    //     return false;
113

114
    return true;
468✔
115
}
116

117
std::shared_ptr<Section> Morphology::appendRootSection(const morphio::Section& section_,
104✔
118
                                                       bool recursive) {
119
    std::shared_ptr<Section> ptr(new Section(this, _counter, section_));
104✔
120
    _register(ptr);
104✔
121
    _rootSections.push_back(ptr);
104✔
122

123
    const bool emptySection = ptr->points().empty();
104✔
124
    if (emptySection) {
104✔
125
        printError(Warning::APPENDING_EMPTY_SECTION, _err.WARNING_APPENDING_EMPTY_SECTION(ptr));
×
126
    }
127

128
    if (recursive) {
104✔
129
        for (const auto& child : section_.children()) {
256✔
130
            ptr->appendSection(child, true);
152✔
131
        }
132
    }
133

134
    return ptr;
104✔
135
}
136

137
std::shared_ptr<Section> Morphology::appendRootSection(const std::shared_ptr<Section>& section_,
×
138
                                                       bool recursive) {
139
    std::shared_ptr<Section> section_copy(new Section(this, _counter, *section_));
×
140
    _register(section_copy);
×
141
    _rootSections.push_back(section_copy);
×
142
    const bool emptySection = section_copy->points().empty();
×
143
    if (emptySection) {
×
144
        printError(Warning::APPENDING_EMPTY_SECTION,
×
145
                   _err.WARNING_APPENDING_EMPTY_SECTION(section_copy));
×
146
    }
147

148
    if (recursive) {
×
149
        for (const auto& child : section_->children()) {
×
150
            section_copy->appendSection(child, true);
×
151
        }
152
    }
153

154
    return section_copy;
×
155
}
156

157
std::shared_ptr<Section> Morphology::appendRootSection(const Property::PointLevel& pointProperties,
206✔
158
                                                       SectionType type) {
159
    std::shared_ptr<Section> ptr(new Section(this, _counter, type, pointProperties));
206✔
160
    _register(ptr);
206✔
161
    _rootSections.push_back(ptr);
206✔
162

163
    bool emptySection = ptr->points().empty();
206✔
164
    if (emptySection) {
206✔
165
        printError(Warning::APPENDING_EMPTY_SECTION, _err.WARNING_APPENDING_EMPTY_SECTION(ptr));
×
166
    }
167

168
    return ptr;
206✔
169
}
170

171
uint32_t Morphology::_register(const std::shared_ptr<Section>& section_) {
770✔
172
    if (_sections.count(section_->id()) > 0) {
770✔
173
        throw SectionBuilderError("Section already exists");
×
174
    }
175
    _counter = std::max(_counter, section_->id()) + 1;
770✔
176
    _sections[section_->id()] = section_;
770✔
177
    return section_->id();
770✔
178
}
179

180
Morphology::~Morphology() {
186✔
181
    auto roots = _rootSections;  // Need to iterate on a copy
372✔
182
    for (const auto& root : roots) {
498✔
183
        deleteSection(root, true);
312✔
184
    }
185
}
186✔
186

187
void Morphology::eraseByValue(std::vector<std::shared_ptr<Section>>& vec,
770✔
188
                              const std::shared_ptr<Section> section) {
189
    if (section->morphology_ == this) {
770✔
190
        section->morphology_ = nullptr;
770✔
191
        section->id_ = 0xffffffff;
770✔
192
    }
193
    vec.erase(std::remove(vec.begin(), vec.end(), section), vec.end());
770✔
194
}
770✔
195

196

197
void Morphology::deleteSection(const std::shared_ptr<Section> section_, bool recursive) {
1,082✔
198
    if (!section_) {
1,082✔
199
        return;
×
200
    }
201

202
    if (recursive) {
1,082✔
203
        // The deletion must start by the furthest leaves, otherwise you may cut
204
        // the topology and forget to remove some sections
205
        std::vector<std::shared_ptr<Section>> ids;
624✔
206
        std::copy(section_->breadth_begin(), breadth_end(), std::back_inserter(ids));
312✔
207

208
        for (auto it = ids.rbegin(); it != ids.rend(); ++it) {
1,072✔
209
            deleteSection(*it, false);
760✔
210
        }
211
    } else {
212
        const auto section_id = section_->id();
770✔
213

214
        // Careful not to use a reference here or you will face reference invalidation problem
215
        // with vector resize
216
        const auto children = section_->children();
1,540✔
217

218
        if (section_->isRoot()) {
770✔
219
            // put root section's children in its place
220
            insertSectionsBeforeSection(_rootSections, children, section_);
314✔
221
            eraseByValue(_rootSections, section_);
314✔
222
        } else {
223
            // set grandparent as section children's parent
224
            for (const auto& child : children) {
460✔
225
                _parent[child->id()] = _parent[section_id];
4✔
226
            }
227

228
            auto& children_of_parent = _children[_parent[section_id]];
456✔
229

230
            // put grandchildren at the position of the deleted section
231
            insertSectionsBeforeSection(children_of_parent, children, section_);
456✔
232
            eraseByValue(children_of_parent, section_);
456✔
233
        }
234

235
        // remove section id from connectivity and section lists
236
        _children.erase(section_id);
770✔
237
        _parent.erase(section_id);
770✔
238
        _sections.erase(section_id);
770✔
239
    }
240
}
241

242
void Morphology::removeUnifurcations() {
4✔
243
    removeUnifurcations(morphio::readers::DebugInfo());
4✔
244
}
4✔
245

246
void Morphology::removeUnifurcations(const morphio::readers::DebugInfo& debugInfo) {
4✔
247
    morphio::readers::ErrorMessages err(debugInfo._filename);
8✔
248

249
    auto it = depth_begin();
8✔
250
    while (it != depth_end()) {
16✔
251
        std::shared_ptr<Section> section_ = *it;
12✔
252

253
        // Incrementing iterator here before we potentially delete the section
254
        ++it;
12✔
255
        unsigned int sectionId = section_->id();
12✔
256

257
        if (section_->isRoot()) {
12✔
258
            continue;
4✔
259
        }
260

261
        unsigned int parentId = section_->parent()->id();
8✔
262

263
        if (!ErrorMessages::isIgnored(Warning::WRONG_DUPLICATE) &&
16✔
264
            !_checkDuplicatePoint(section_->parent(), section_)) {
8✔
265
            printError(Warning::WRONG_DUPLICATE,
×
266
                       err.WARNING_WRONG_DUPLICATE(section_, section_->parent()));
×
267
        }
268

269
        auto parent = section_->parent();
16✔
270
        bool isUnifurcation = parent->children().size() == 1;
8✔
271

272
        // This "if" condition ensures that "unifurcations" (ie. successive
273
        // sections with only 1 child) get merged together into a bigger section
274
        if (isUnifurcation) {
8✔
275
            printError(Warning::ONLY_CHILD, err.WARNING_ONLY_CHILD(debugInfo, parentId, sectionId));
8✔
276
            bool duplicate = _checkDuplicatePoint(section_->parent(), section_);
8✔
277

278
            addAnnotation(morphio::Property::Annotation(morphio::AnnotationType::SINGLE_CHILD,
16✔
279
                                                        sectionId,
280
                                                        section_->properties(),
8✔
281
                                                        "",
282
                                                        debugInfo.getLineNumber(parentId)));
283

284
            morphio::_appendVector(parent->points(), section_->points(), duplicate ? 1 : 0);
8✔
285

286
            morphio::_appendVector(parent->diameters(), section_->diameters(), duplicate ? 1 : 0);
8✔
287

288
            if (!parent->perimeters().empty()) {
8✔
289
                morphio::_appendVector(parent->perimeters(),
×
290
                                       section_->perimeters(),
×
291
                                       duplicate ? 1 : 0);
292
            }
293

294
            deleteSection(section_, false);
8✔
295
        }
296
    }
297
}
4✔
298

299
Property::Properties Morphology::buildReadOnly() const {
110✔
300
    int sectionIdOnDisk = 0;
110✔
301
    std::map<uint32_t, int32_t> newIds;
220✔
302
    Property::Properties properties{};
110✔
303

304
    properties._cellLevel = *_cellProperties;
110✔
305
    properties._cellLevel._somaType = _soma->type();
110✔
306
    appendProperties(properties._somaLevel, _soma->point_properties_);
110✔
307

308
    for (auto it = depth_begin(); it != depth_end(); ++it) {
662✔
309
        const std::shared_ptr<Section>& section = *it;
1,104✔
310
        unsigned int sectionId = section->id();
552✔
311
        int parentOnDisk = (section->isRoot() ? -1 : newIds[section->parent()->id()]);
552✔
312

313
        auto start = static_cast<int>(properties._pointLevel._points.size());
552✔
314
        properties._sectionLevel._sections.push_back({start, parentOnDisk});
552✔
315
        properties._sectionLevel._sectionTypes.push_back(section->type());
552✔
316
        newIds[sectionId] = sectionIdOnDisk++;
552✔
317
        appendProperties(properties._pointLevel, section->point_properties_);
552✔
318
    }
319

320
    mitochondria()._buildMitochondria(properties);
110✔
321
    properties._endoplasmicReticulumLevel = endoplasmicReticulum().buildReadOnly();
110✔
322
    properties._dendriticSpineLevel = _dendriticSpineLevel;
110✔
323

324
    return properties;
220✔
325
}
326

327
depth_iterator Morphology::depth_begin() const {
120✔
328
    return depth_iterator(*this);
120✔
329
}
330

331
depth_iterator Morphology::depth_end() const {
720✔
332
    return depth_iterator();
720✔
333
}
334

335
breadth_iterator Morphology::breadth_begin() const {
×
336
    return breadth_iterator(*this);
×
337
}
338

339
breadth_iterator Morphology::breadth_end() const {
312✔
340
    return breadth_iterator();
312✔
341
}
342

343
void Morphology::applyModifiers(unsigned int modifierFlags) {
172✔
344
    if (modifierFlags & NO_DUPLICATES & TWO_POINTS_SECTIONS) {
345
        throw SectionBuilderError(
346
            _err.ERROR_UNCOMPATIBLE_FLAGS(NO_DUPLICATES, TWO_POINTS_SECTIONS));
347
    }
348

349
    if (modifierFlags & SOMA_SPHERE) {
172✔
350
        modifiers::soma_sphere(*this);
×
351
    }
352

353
    if (modifierFlags & NO_DUPLICATES) {
172✔
354
        modifiers::no_duplicate_point(*this);
×
355
    }
356

357
    if (modifierFlags & TWO_POINTS_SECTIONS) {
172✔
358
        modifiers::two_points_sections(*this);
×
359
    }
360

361
    if (modifierFlags & NRN_ORDER) {
172✔
362
        modifiers::nrn_order(*this);
6✔
363
    }
364
}
172✔
365

366
std::unordered_map<int, std::vector<unsigned int>> Morphology::connectivity() {
2✔
367
    std::unordered_map<int, std::vector<unsigned int>> connectivity;
2✔
368

369
    const auto& roots = rootSections();
2✔
370
    connectivity[-1].reserve(roots.size());
2✔
371
    std::transform(roots.begin(),
372
                   roots.end(),
373
                   std::back_inserter(connectivity[-1]),
2✔
374
                   [](const std::shared_ptr<Section>& section) { return section->id(); });
6✔
375

376
    for (const auto& kv : _children) {
6✔
377
        auto& nodeEdges = connectivity[static_cast<int>(kv.first)];
4✔
378
        nodeEdges.reserve(kv.second.size());
4✔
379
        std::transform(kv.second.begin(),
380
                       kv.second.end(),
381
                       std::back_inserter(nodeEdges),
382
                       [](const std::shared_ptr<Section>& section) { return section->id(); });
12✔
383
    }
384

385
    return connectivity;
2✔
386
}
387

388
void Morphology::write(const std::string& filename) const {
6✔
389
    for (const auto& root : rootSections()) {
18✔
390
        if (root->points().size() < 2) {
12✔
391
            throw morphio::SectionBuilderError("Root sections must have at least 2 points");
×
392
        }
393
    }
394

395
    const size_t pos = filename.find_last_of('.');
6✔
396
    if (pos == std::string::npos) {
6✔
397
        throw UnknownFileType("Missing file extension.");
×
398
    }
399

400
    std::string extension;
12✔
401
    for (char c : filename.substr(pos)) {
28✔
402
        extension += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
22✔
403
    }
404

405
    if (extension == ".h5") {
6✔
406
        writer::h5(*this, filename);
2✔
407
    } else if (extension == ".asc") {
4✔
408
        writer::asc(*this, filename);
2✔
409
    } else if (extension == ".swc") {
2✔
410
        writer::swc(*this, filename);
2✔
411
    } else {
412
        throw UnknownFileType(_err.ERROR_WRONG_EXTENSION(filename));
×
413
    }
414
}
6✔
415

416
}  // end namespace mut
417
}  // end namespace morphio
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

© 2026 Coveralls, Inc