diff --git a/src/cell.cpp b/src/cell.cpp index a6986f0673ca05827ee89493aca2230ec16cec30..8fdd1bb6e0f55cdfa2a3c7ea6632e0fdee74bba6 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -195,5 +195,41 @@ std::vector<int> const& cell::segment_parents() const return parents_; } +// Rough and ready comparison of two cells. +// We don't use an operator== because equality of two cells is open to +// interpretation. For example, it is possible to have two viable representations +// of a cell: with and without location information for the cables. +// +// Checks that two cells have the same +// - number and type of segments +// - volume and area properties of each segment +// - number of compartments in each segment +bool cell_basic_equality(cell const& lhs, cell const& rhs) +{ + if(lhs.num_segments() != rhs.num_segments()) { + return false; + } + if(lhs.segment_parents() != rhs.segment_parents()) { + return false; + } + for(auto i=0; i<lhs.num_segments(); ++i) { + // a quick and dirty test + auto& l = *lhs.segment(i); + auto& r = *rhs.segment(i); + + if(l.kind() != r.kind()) return false; + if(l.area() != r.area()) return false; + if(l.volume() != r.volume()) return false; + if(l.as_cable()) { + if( l.as_cable()->num_compartments() + != r.as_cable()->num_compartments()) + { + return false; + } + } + } + return true; +} + } // namespace mc } // namespace nest diff --git a/src/cell.hpp b/src/cell.hpp index ba5f9b6bff227231cbb4f46e27dd2af4ff2943ce..fece134947041d705564008e54be61ce93d50f60 100644 --- a/src/cell.hpp +++ b/src/cell.hpp @@ -123,6 +123,12 @@ class cell { std::vector<std::pair<segment_location, i_clamp>> stimulii_; }; +// Checks that two cells have the same +// - number and type of segments +// - volume and area properties of each segment +// - number of compartments in each segment +bool cell_basic_equality(cell const& lhs, cell const& rhs); + // create a cable by forwarding cable construction parameters provided by the user template <typename... Args> cable_segment* cell::add_cable(cell::index_type parent, Args ...args) diff --git a/src/segment.hpp b/src/segment.hpp index fd1d8ded6101cc8e9834b9062df3da97de5cbc8c..decb0cdd88665c421cf159b8d7f49e4f91e03f0f 100644 --- a/src/segment.hpp +++ b/src/segment.hpp @@ -76,6 +76,16 @@ class segment { return nullptr; } + virtual const cable_segment* as_cable() const + { + return nullptr; + } + + virtual const soma_segment* as_soma() const + { + return nullptr; + } + virtual bool is_placeholder() const { return false; @@ -230,6 +240,11 @@ class soma_segment : public segment return this; } + const soma_segment* as_soma() const override + { + return this; + } + /// soma has one and one only compartments int num_compartments() const override { @@ -359,6 +374,11 @@ class cable_segment : public segment return this; } + const cable_segment* as_cable() const override + { + return this; + } + int num_compartments() const override { return num_compartments_; diff --git a/src/util.hpp b/src/util.hpp index 706ab7da5a17d493823b8c74d1f18fa6b4a9efaf..afa029ff9cfcba65258473875003c00f58c3d16b 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -45,6 +45,21 @@ std::ostream& print(std::ostream &o, std::vector<T>const& v) return o; } +template <typename T> +bool operator ==(const std::vector<T>& lhs, const std::vector<T>& rhs) +{ + if(lhs.size() != rhs.size()) { + return false; + } + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template <typename T> +bool operator !=(const std::vector<T>& lhs, const std::vector<T>& rhs) +{ + return !(lhs==rhs); +} + namespace nest { namespace mc { namespace util { diff --git a/tests/test_swcio.cpp b/tests/test_swcio.cpp index 679260d656c9c7964f0eea599951c371886123f4..6968fd432a89842023807c8ed0ffb2ca8140b231 100644 --- a/tests/test_swcio.cpp +++ b/tests/test_swcio.cpp @@ -487,3 +487,31 @@ TEST(swc_io, cell_construction) cell.cable(3)->radius(0)); } } + +// check that simple ball and stick model with one dendrite attached to a soma +// which is used in the validation tests can be loaded from file and matches +// the one generated with the C++ interface +TEST(swc_parser, from_file_ball_and_stick) +{ + auto fname = "../data/ball_and_stick.swc"; + std::ifstream fid(fname); + if(!fid.is_open()) { + std::cerr << "unable to open file " << fname << "... skipping test\n"; + return; + } + + // read the file into a cell object + auto cell = nest::mc::io::swc_read_cell(fid); + + // verify that the correct number of nodes was read + EXPECT_EQ(cell.num_segments(), 2); + EXPECT_EQ(cell.num_compartments(), 2u); + + // make an equivalent cell via C++ interface + nest::mc::cell local_cell; + local_cell.add_soma(6.30785); + local_cell.add_cable(0, nest::mc::segmentKind::dendrite, 0.5, 0.5, 200); + + EXPECT_TRUE(nest::mc::cell_basic_equality(local_cell, cell)); +} +