diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d082f12be8d6c555372be33a8737959f4e2a08b..06a4927e4c2eb7448ee8f07e4c44e3750d3153ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ -cmake_minimum_required (VERSION 2.8) +cmake_minimum_required(VERSION 2.8) # project info -project (cell_algorithms) +project(cell_algorithms) enable_language(CXX) # save incoming CXX flags for forwarding to modparser external project @@ -19,6 +19,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS "YES") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +# enable assertions? +set(WITH_ASSERTIONS OFF CACHE BOOL "enable EXPECTS() assertions in code") +if(WITH_ASSERTIONS) + add_definitions("-DWITH_ASSERTIONS") +endif() + # TBB support set(WITH_TBB OFF CACHE BOOL "use TBB for on-node threading" ) if(WITH_TBB) diff --git a/data/ball_and_stick.swc b/data/ball_and_stick.swc index 1737539b1b4d73d202cecab2ecb7e5599d93064e..298398678a91ec8141dd60ce77796dc54155d3f3 100644 --- a/data/ball_and_stick.swc +++ b/data/ball_and_stick.swc @@ -3,6 +3,5 @@ # - dendrite with length 200 and radius 0.5 1 1 0.0 0.0 0.0 6.30785 -1 -2 2 6.30785 0.0 0.0 0.5 1 -3 2 206.30785 0.0 0.0 0.5 2 - +2 3 6.30785 0.0 0.0 0.5 1 +3 3 206.30785 0.0 0.0 0.5 2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e7bced8530908d637ecec5112fdb5e581e652e9a..7f4a588d933a5ea74342c70dc8b6ac5df95aca8b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ set(BASE_SOURCES parameter_list.cpp profiling/profiler.cpp swcio.cpp + util/debug.cpp ) if(${WITH_MPI}) diff --git a/src/algorithms.hpp b/src/algorithms.hpp index 738f163fa8381363b099c981d57df36923bc921d..e89f06f539f51b37f7ce95ca8ff2de18b3709d62 100644 --- a/src/algorithms.hpp +++ b/src/algorithms.hpp @@ -7,6 +7,7 @@ #include <vector> #include "util.hpp" +#include "util/debug.hpp" /* * Some simple wrappers around stl algorithms to improve readability of code @@ -248,7 +249,7 @@ std::vector<typename C::value_type> make_parent_index( return {}; } - EXPECTS(parent_index.size() == branch_index.back()); + EXPECTS(parent_index.size() == unsigned(branch_index.back())); EXPECTS(has_contiguous_segments(parent_index)); EXPECTS(is_strictly_monotonic_increasing(branch_index)); diff --git a/src/cell.cpp b/src/cell.cpp index fb9147ed5e52552da3dc55b3198b5b50d50b7f19..3a2d5672a12bb74e2e484765e16b93e870b2bd4b 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,5 +1,6 @@ #include "cell.hpp" #include "tree.hpp" +#include "util/debug.hpp" namespace nest { namespace mc { @@ -8,7 +9,7 @@ int find_compartment_index( segment_location const& location, compartment_model const& graph ) { - EXPECTS(location.segment<graph.segment_index.size()); + EXPECTS(unsigned(location.segment)<graph.segment_index.size()); const auto& si = graph.segment_index; const auto seg = location.segment; diff --git a/src/cell.hpp b/src/cell.hpp index 1c047e18b2b90cba58a805f66f1579723dd0ccd5..744c9970e26af7f439f9cee7cfb2f88c96014624 100644 --- a/src/cell.hpp +++ b/src/cell.hpp @@ -8,6 +8,7 @@ #include "segment.hpp" #include "cell_tree.hpp" #include "stimulus.hpp" +#include "util/debug.hpp" namespace nest { namespace mc { diff --git a/src/fvm_cell.hpp b/src/fvm_cell.hpp index dde7637cea2221aa2e7d519d4a7c6bccdcc9599d..25c0ae9a0c67dd42e7bcf680a9b32a0c6070290b 100644 --- a/src/fvm_cell.hpp +++ b/src/fvm_cell.hpp @@ -141,6 +141,10 @@ public: return (v>-1000.) && (v<1000.); } + /// returns current at a segment location + value_type current(segment_location loc) const; + + value_type time() const { return t_; } value_type probe(uint32_t i) const { @@ -476,7 +480,7 @@ void fvm_cell<T, I>::setup_matrix(T dt) template <typename T, typename I> int fvm_cell<T, I>::compartment_index(segment_location loc) const { - EXPECTS(loc.segment < segment_index_.size()); + EXPECTS(unsigned(loc.segment) < segment_index_.size()); const auto seg = loc.segment; @@ -492,6 +496,12 @@ T fvm_cell<T, I>::voltage(segment_location loc) const return voltage_[compartment_index(loc)]; } +template <typename T, typename I> +T fvm_cell<T, I>::current(segment_location loc) const +{ + return current_[compartment_index(loc)]; +} + template <typename T, typename I> void fvm_cell<T, I>::initialize() { diff --git a/src/matrix.hpp b/src/matrix.hpp index 9e2c9e7d10d68c2f973cffaaf005ceb939fdcf41..d0ddb816adc5cb9bfb6c3c88562e0a41835920ec 100644 --- a/src/matrix.hpp +++ b/src/matrix.hpp @@ -4,6 +4,7 @@ #include <vector/include/Vector.hpp> #include "util.hpp" +#include "util/debug.hpp" namespace nest { namespace mc { diff --git a/src/profiling/profiler.cpp b/src/profiling/profiler.cpp index c07351e296ba8873ed7064ca4b605d2e9bb0cb8d..b5d2ad0d8d7bd9efe7fd8e55e5f6720ab051b3f1 100644 --- a/src/profiling/profiler.cpp +++ b/src/profiling/profiler.cpp @@ -1,4 +1,7 @@ +#include <numeric> + #include "profiler.hpp" +#include "util/debug.hpp" #include <communication/global_policy.hpp> @@ -372,4 +375,3 @@ void profiler_output(double threshold) {} } // namespace util } // namespace mc } // namespace nest - diff --git a/src/swcio.cpp b/src/swcio.cpp index 203f69eb1bdca8905145edb4edbe86d564682e9b..3f38f8e791308819ac2fe3eeecde4c78ab585799 100644 --- a/src/swcio.cpp +++ b/src/swcio.cpp @@ -9,6 +9,7 @@ #include "point.hpp" #include "swcio.hpp" #include "util.hpp" +#include "util/debug.hpp" namespace nest { namespace mc { @@ -17,7 +18,7 @@ namespace io { // // swc_record implementation // -void swc_record::renumber(id_type new_id, std::map<id_type, id_type> &idmap) +void swc_record::renumber(id_type new_id, std::map<id_type, id_type>& idmap) { auto old_id = id_; id_ = new_id; @@ -58,7 +59,7 @@ void swc_record::check_consistency() const } } -std::istream &operator>>(std::istream &is, swc_record &record) +std::istream& operator>>(std::istream& is, swc_record& record) { swc_parser parser; parser.parse_record(is, record); @@ -66,7 +67,7 @@ std::istream &operator>>(std::istream &is, swc_record &record) } -std::ostream &operator<<(std::ostream &os, const swc_record &record) +std::ostream& operator<<(std::ostream& os, const swc_record& record) { // output in one-based indexing os << record.id_+1 << " " @@ -85,12 +86,28 @@ std::ostream &operator<<(std::ostream &os, const swc_record &record) // Utility functions // -bool starts_with(const std::string &str, const std::string &prefix) +std::string::size_type find_first_non_whitespace(const std::string& str) { - return (str.find(prefix) == 0); + return str.find_first_not_of(" \f\n\r\t\v"); } -void check_parse_status(const std::istream &is, const swc_parser &parser) +bool starts_with(const std::string& str, const std::string& prefix) +{ + // ignore leading whitespace + auto pos = find_first_non_whitespace(str); + if (pos == std::string::npos) { + return false; + } + + return str.find(prefix, pos) == pos; +} + +bool is_space(const std::string& str) +{ + return find_first_non_whitespace(str) == std::string::npos; +} + +void check_parse_status(const std::istream& is, const swc_parser& parser) { if (is.fail()) { // If we try to read past the eof; fail bit will also be set @@ -98,8 +115,23 @@ void check_parse_status(const std::istream &is, const swc_parser &parser) } } +nest::mc::segmentKind convert_kind(const swc_record::kind& kind) +{ + switch (kind) { + case swc_record::kind::soma: + return segmentKind::soma; + case swc_record::kind::dendrite: + return segmentKind::dendrite; + case swc_record::kind::axon: + return segmentKind::axon; + default: + throw swc_parse_error("no known conversion for swc record type", 0); + } +} + + template<typename T> -T parse_value_strict(std::istream &is, const swc_parser &parser) +T parse_value_strict(std::istream& is, const swc_parser& parser) { T val; check_parse_status(is >> val, parser); @@ -110,7 +142,7 @@ T parse_value_strict(std::istream &is, const swc_parser &parser) // specialize parsing for record types template<> -swc_record::kind parse_value_strict(std::istream &is, const swc_parser &parser) +swc_record::kind parse_value_strict(std::istream& is, const swc_parser& parser) { swc_record::id_type val; check_parse_status(is >> val, parser); @@ -123,14 +155,17 @@ swc_record::kind parse_value_strict(std::istream &is, const swc_parser &parser) // swc_parser implementation // -std::istream &swc_parser::parse_record(std::istream &is, swc_record &record) +std::istream& swc_parser::parse_record(std::istream& is, swc_record& record) { while (!is.eof() && !is.bad()) { // consume empty and comment lines first std::getline(is, linebuff_); + ++lineno_; - if (!linebuff_.empty() && !starts_with(linebuff_, comment_prefix_)) + if (!is_space(linebuff_) && + !starts_with(linebuff_, comment_prefix_)) { break; + } } if (is.bad()) { @@ -139,7 +174,7 @@ std::istream &swc_parser::parse_record(std::istream &is, swc_record &record) } if (is.eof() && - (linebuff_.empty() || starts_with(linebuff_, comment_prefix_))) { + (is_space(linebuff_) || starts_with(linebuff_, comment_prefix_))) { // last line is either empty or a comment; don't parse anything return is; } @@ -151,7 +186,7 @@ std::istream &swc_parser::parse_record(std::istream &is, swc_record &record) std::istringstream line(linebuff_); try { record = parse_record(line); - } catch (std::invalid_argument &e) { + } catch (std::invalid_argument& e) { // Rethrow as a parse error throw swc_parse_error(e.what(), lineno_); } @@ -159,7 +194,7 @@ std::istream &swc_parser::parse_record(std::istream &is, swc_record &record) return is; } -swc_record swc_parser::parse_record(std::istringstream &is) +swc_record swc_parser::parse_record(std::istringstream& is) { auto id = parse_value_strict<int>(is, *this); auto type = parse_value_strict<swc_record::kind>(is, *this); @@ -178,7 +213,7 @@ swc_record swc_parser::parse_record(std::istringstream &is) } -swc_record_range_clean::swc_record_range_clean(std::istream &is) +swc_record_range_clean::swc_record_range_clean(std::istream& is) { std::unordered_set<swc_record::id_type> ids; @@ -212,7 +247,7 @@ swc_record_range_clean::swc_record_range_clean(std::istream &is) // Renumber records if necessary std::map<swc_record::id_type, swc_record::id_type> idmap; swc_record::id_type next_id = 0; - for (auto &r : records_) { + for (auto& r : records_) { if (r.id() != next_id) { r.renumber(next_id, idmap); } @@ -231,14 +266,14 @@ swc_record_range_clean::swc_record_range_clean(std::istream &is) } } -cell swc_read_cell(std::istream &is) +cell swc_read_cell(std::istream& is) { using namespace nest::mc; cell newcell; std::vector<swc_record::id_type> parent_index; std::vector<swc_record> swc_records; - for (const auto &r : swc_get_records<swc_io_clean>(is)) { + for (const auto& r : swc_get_records<swc_io_clean>(is)) { swc_records.push_back(r); parent_index.push_back(r.parent()); } @@ -284,7 +319,7 @@ cell swc_read_cell(std::istream &is) // add the new cable newcell.add_cable(new_parent_index[i], - nest::mc::segmentKind::dendrite, radii, points); + convert_kind(b_start->type()), radii, points); } return newcell; diff --git a/src/swcio.hpp b/src/swcio.hpp index 70f979cda362f503a81965c274a5cdeb23ea5056..6530cba35190049d58be20dcebdcf9d81570053e 100644 --- a/src/swcio.hpp +++ b/src/swcio.hpp @@ -13,8 +13,7 @@ namespace nest { namespace mc { namespace io { -class swc_record -{ +class swc_record { public: using id_type = int; using coord_type = double; @@ -56,10 +55,10 @@ public: , parent_id_(-1) { } - swc_record(const swc_record &other) = default; - swc_record &operator=(const swc_record &other) = default; + swc_record(const swc_record& other) = default; + swc_record& operator=(const swc_record& other) = default; - bool strict_equals(const swc_record &other) const + bool strict_equals(const swc_record& other) const { return id_ == other.id_ && x_ == other.x_ && @@ -70,43 +69,43 @@ public: } // Equality and comparison operators - friend bool operator==(const swc_record &lhs, - const swc_record &rhs) + friend bool operator==(const swc_record& lhs, + const swc_record& rhs) { return lhs.id_ == rhs.id_; } - friend bool operator<(const swc_record &lhs, - const swc_record &rhs) + friend bool operator<(const swc_record& lhs, + const swc_record& rhs) { return lhs.id_ < rhs.id_; } - friend bool operator<=(const swc_record &lhs, - const swc_record &rhs) + friend bool operator<=(const swc_record& lhs, + const swc_record& rhs) { return (lhs < rhs) || (lhs == rhs); } - friend bool operator!=(const swc_record &lhs, - const swc_record &rhs) + friend bool operator!=(const swc_record& lhs, + const swc_record& rhs) { return !(lhs == rhs); } - friend bool operator>(const swc_record &lhs, - const swc_record &rhs) + friend bool operator>(const swc_record& lhs, + const swc_record& rhs) { return !(lhs < rhs) && (lhs != rhs); } - friend bool operator>=(const swc_record &lhs, - const swc_record &rhs) + friend bool operator>=(const swc_record& lhs, + const swc_record& rhs) { return !(lhs < rhs); } - friend std::ostream &operator<<(std::ostream &os, const swc_record &record); + friend std::ostream& operator<<(std::ostream& os, const swc_record& record); kind type() const { @@ -153,7 +152,7 @@ public: return nest::mc::point<coord_type>(x_, y_, z_); } - void renumber(id_type new_id, std::map<id_type, id_type> &idmap); + void renumber(id_type new_id, std::map<id_type, id_type>& idmap); private: void check_consistency() const; @@ -169,12 +168,12 @@ private: class swc_parse_error : public std::runtime_error { public: - explicit swc_parse_error(const char *msg, std::size_t lineno) + explicit swc_parse_error(const char* msg, std::size_t lineno) : std::runtime_error(msg) , lineno_(lineno) { } - explicit swc_parse_error(const std::string &msg, std::size_t lineno) + explicit swc_parse_error(const std::string& msg, std::size_t lineno) : std::runtime_error(msg) , lineno_(lineno) { } @@ -188,11 +187,9 @@ private: std::size_t lineno_; }; -class swc_parser -{ +class swc_parser { public: - swc_parser(const std::string &delim, - std::string comment_prefix) + swc_parser(const std::string& delim, std::string comment_prefix) : delim_(delim) , comment_prefix_(comment_prefix) , lineno_(0) @@ -209,11 +206,11 @@ public: return lineno_; } - std::istream &parse_record(std::istream &is, swc_record &record); + std::istream& parse_record(std::istream& is, swc_record& record); private: // Read the record from a string stream; will be treated like a single line - swc_record parse_record(std::istringstream &is); + swc_record parse_record(std::istringstream& is); std::string delim_; std::string comment_prefix_; @@ -222,15 +219,14 @@ private: }; -std::istream &operator>>(std::istream &is, swc_record &record); +std::istream& operator>>(std::istream& is, swc_record& record); class swc_record_stream_iterator : - public std::iterator<std::forward_iterator_tag, swc_record> -{ + public std::iterator<std::forward_iterator_tag, swc_record> { public: struct eof_tag { }; - swc_record_stream_iterator(std::istream &is) + swc_record_stream_iterator(std::istream& is) : is_(is) , eof_(false) { @@ -239,19 +235,19 @@ public: read_next_record(); } - swc_record_stream_iterator(std::istream &is, eof_tag) + swc_record_stream_iterator(std::istream& is, eof_tag) : is_(is) , eof_(true) { } - swc_record_stream_iterator(const swc_record_stream_iterator &other) + swc_record_stream_iterator(const swc_record_stream_iterator& other) : is_(other.is_) , parser_(other.parser_) , curr_record_(other.curr_record_) , eof_(other.eof_) { } - swc_record_stream_iterator &operator++() + swc_record_stream_iterator& operator++() { if (eof_) { throw std::out_of_range("attempt to read past eof"); @@ -277,7 +273,7 @@ public: return curr_record_; } - bool operator==(const swc_record_stream_iterator &other) const + bool operator==(const swc_record_stream_iterator& other) const { if (eof_ && other.eof_) { return true; @@ -286,13 +282,13 @@ public: } } - bool operator!=(const swc_record_stream_iterator &other) const + bool operator!=(const swc_record_stream_iterator& other) const { return !(*this == other); } - friend std::ostream &operator<<(std::ostream &os, - const swc_record_stream_iterator &iter) + friend std::ostream& operator<<(std::ostream& os, + const swc_record_stream_iterator& iter) { os << "{ is_.tellg(): " << iter.is_.tellg() << ", " << "curr_record_: " << iter.curr_record_ << ", " @@ -310,7 +306,7 @@ private: } } - std::istream &is_; + std::istream& is_; swc_parser parser_; swc_record curr_record_; @@ -320,16 +316,15 @@ private: }; -class swc_record_range_raw -{ +class swc_record_range_raw { public: using value_type = swc_record; - using reference = value_type &; - using const_reference = const value_type &; + using reference = value_type&; + using const_reference = const value_type&; using iterator = swc_record_stream_iterator; using const_iterator = const swc_record_stream_iterator; - swc_record_range_raw(std::istream &is) + swc_record_range_raw(std::istream& is) : is_(is) { } @@ -350,7 +345,7 @@ public: } private: - std::istream &is_; + std::istream& is_; }; // @@ -361,16 +356,15 @@ private: // https://github.com/eth-cscs/cell_algorithms/wiki/SWC-file-parsing // -class swc_record_range_clean -{ +class swc_record_range_clean { public: using value_type = swc_record; - using reference = value_type &; - using const_referene = const value_type &; + using reference = value_type&; + using const_referene = const value_type&; using iterator = std::vector<swc_record>::iterator; using const_iterator = std::vector<swc_record>::const_iterator; - swc_record_range_clean(std::istream &is); + swc_record_range_clean(std::istream& is); iterator begin() { @@ -407,12 +401,12 @@ struct swc_io_clean }; template<typename T = swc_io_clean> -typename T::record_range_type swc_get_records(std::istream &is) +typename T::record_range_type swc_get_records(std::istream& is) { return typename T::record_range_type(is); } -cell swc_read_cell(std::istream &is); +cell swc_read_cell(std::istream& is); } // namespace io } // namespace mc diff --git a/src/util.hpp b/src/util.hpp index c4362b25d0ad9e54e962b118e84ce974b422a636..29a6b28a9ec09cce13fcced0db20ae30aa3d803f 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -2,12 +2,6 @@ #include <vector/include/Vector.hpp> -#ifdef DEBUG -#define EXPECTS(expression) assert(expression) -#else -#define EXPECTS(expression) -#endif - /* using memory::util::red; using memory::util::yellow; diff --git a/src/util/debug.cpp b/src/util/debug.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2dd83f562d3ea15477d7507026a370a87428a36b --- /dev/null +++ b/src/util/debug.cpp @@ -0,0 +1,16 @@ +#include <cstdlib> +#include <iostream> + +#include "util/debug.hpp" + +bool nest::mc::util::failed_assertion(const char *assertion, const char *file, + int line, const char *func) +{ + // Explicit flush, as we can't assume default buffering semantics on stderr/cerr, + // and abort() might not flush streams. + + std::cerr << file << ':' << line << " " << func + << ": Assertion `" << assertion << "' failed." << std::endl; + std::abort(); + return false; +} diff --git a/src/util/debug.hpp b/src/util/debug.hpp new file mode 100644 index 0000000000000000000000000000000000000000..148b3c44db9b03cd396c10db044e6924b99d7104 --- /dev/null +++ b/src/util/debug.hpp @@ -0,0 +1,30 @@ +#pragma once + +namespace nest { +namespace mc { +namespace util { + +bool failed_assertion(const char *assertion, const char *file, int line, const char *func); + +} +} +} + + +#ifdef WITH_ASSERTIONS + +#ifdef __GNUC__ +#define DEBUG_FUNCTION_NAME __PRETTY_FUNCTION__ +#else +#define DEBUG_FUNCTION_NAME __func__ +#endif + +#define EXPECTS(condition) \ +(void)((condition) || \ + nest::mc::util::failed_assertion(#condition, __FILE__, __LINE__, DEBUG_FUNCTION_NAME)) + +#else + +#define EXPECTS(condition) + +#endif // def WITH_ASSERTIONS diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ebebb306e1a896f1cd815751324971ea4e67cfe8..9061fb8d4698c681d3cdd38986b081f6b47df5aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,6 +24,7 @@ set(TEST_SOURCES test_optional.cpp test_parameters.cpp test_point.cpp + test_probe.cpp test_segment.cpp test_spikes.cpp test_stimulus.cpp diff --git a/tests/test_algorithms.cpp b/tests/test_algorithms.cpp index ec25bd42c0941b8324fbea3288ed5b4f1d1f7a59..d1ffdf11811f44266df10215026a11d907172459 100644 --- a/tests/test_algorithms.cpp +++ b/tests/test_algorithms.cpp @@ -1,9 +1,11 @@ #include <vector> #include "gtest.h" + +#include "algorithms.hpp" #include "util.hpp" +#include "util/debug.hpp" -#include "../src/algorithms.hpp" TEST(algorithms, sum) { diff --git a/tests/test_probe.cpp b/tests/test_probe.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52ac4e7f62171bc556df5b0ba4e25f05b3304fa5 --- /dev/null +++ b/tests/test_probe.cpp @@ -0,0 +1,78 @@ +#include "gtest.h" + +#include "cell.hpp" +#include "fvm_cell.hpp" + +TEST(probe, instantiation) +{ + using namespace nest::mc; + + cell c1; + + segment_location loc1{0, 0}; + segment_location loc2{1, 0.6}; + + auto p1 = c1.add_probe(loc1, probeKind::membrane_voltage); + auto p2 = c1.add_probe(loc2, probeKind::membrane_current); + + // expect locally provided probe ids to be numbered sequentially from zero. + + EXPECT_EQ(0, p1); + EXPECT_EQ(1, p2); + + // expect the probes() return to be a collection with these two probes. + + auto probes = c1.probes(); + EXPECT_EQ(2u, probes.size()); + + EXPECT_EQ(loc1, probes[0].location); + EXPECT_EQ(probeKind::membrane_voltage, probes[0].kind); + + EXPECT_EQ(loc2, probes[1].location); + EXPECT_EQ(probeKind::membrane_current, probes[1].kind); +} + +TEST(probe, fvm_cell) +{ + using namespace nest::mc; + + cell bs; + + // ball-and-stick model morphology + + bs.add_soma(12.6157/2.0); + bs.add_cable(0, segmentKind::dendrite, 0.5, 0.5, 200); + bs.soma()->set_compartments(5); + + segment_location loc0{0, 0}; + segment_location loc1{1, 1}; + segment_location loc2{1, 0.5}; + + auto pv0 = bs.add_probe(loc0, probeKind::membrane_voltage); + auto pv1 = bs.add_probe(loc1, probeKind::membrane_voltage); + auto pi2 = bs.add_probe(loc2, probeKind::membrane_current); + + i_clamp stim(0, 100, 0.3); + bs.add_stimulus({1, 1}, stim); + + fvm::fvm_cell<double, int> lcell(bs); + lcell.setup_matrix(0.01); + lcell.initialize(); + + EXPECT_EQ(3u, lcell.num_probes()); + + // expect probe values and direct queries of voltage and current + // to be equal in fvm cell + + EXPECT_EQ(lcell.voltage(loc0), lcell.probe(pv0)); + EXPECT_EQ(lcell.voltage(loc1), lcell.probe(pv1)); + EXPECT_EQ(lcell.current(loc2), lcell.probe(pi2)); + + lcell.advance(0.05); + + EXPECT_EQ(lcell.voltage(loc0), lcell.probe(pv0)); + EXPECT_EQ(lcell.voltage(loc1), lcell.probe(pv1)); + EXPECT_EQ(lcell.current(loc2), lcell.probe(pi2)); +} + + diff --git a/tests/test_swcio.cpp b/tests/test_swcio.cpp index 5ba0b991a865dfb397e5c1dbbb1e8813e71b628c..32eaff75b967ccf9c5f8b7ef7f06c8b3d8b10f3e 100644 --- a/tests/test_swcio.cpp +++ b/tests/test_swcio.cpp @@ -13,12 +13,12 @@ // Path to data directory can be overriden at compile time. #if !defined(DATADIR) -#define DATADIR "../data" +# define DATADIR "../data" #endif // SWC tests -void expect_record_equals(const nest::mc::io::swc_record &expected, - const nest::mc::io::swc_record &actual) +void expect_record_equals(const nest::mc::io::swc_record& expected, + const nest::mc::io::swc_record& actual) { EXPECT_EQ(expected.id(), actual.id()); EXPECT_EQ(expected.type(), actual.type()); @@ -159,7 +159,7 @@ TEST(swc_parser, invalid_input) } FAIL() << "expected swc_parse_error, none was thrown\n"; - } catch (const swc_parse_error &e) { + } catch (const swc_parse_error& e) { SUCCEED(); } } @@ -187,6 +187,58 @@ TEST(swc_parser, valid_input) expect_record_equals(record_orig, record); } + { + // check comment not starting at first character + swc_record record, record_orig; + std::istringstream is(" #comment"); + EXPECT_NO_THROW(is >> record); + expect_record_equals(record_orig, record); + } + + { + // check whitespace lines + swc_record record; + std::stringstream is; + is << "#comment\n"; + is << " \t\n"; + is << "1 1 14.566132 34.873772 7.857000 0.717830 -1\n"; + + EXPECT_NO_THROW(is >> record); + swc_record record_expected( + swc_record::kind::soma, + 0, 14.566132, 34.873772, 7.857000, 0.717830, -1); + + expect_record_equals(record_expected, record); + } + + { + // check windows eol + swc_record record; + std::stringstream is; + is << "#comment\r\n"; + is << "\r\n"; + is << "1 1 14.566132 34.873772 7.857000 0.717830 -1\r\n"; + + EXPECT_NO_THROW(is >> record); + swc_record record_expected( + swc_record::kind::soma, + 0, 14.566132, 34.873772, 7.857000, 0.717830, -1); + + expect_record_equals(record_expected, record); + } + + { + // check old-style mac eol; these eol are treated as simple whitespace + // characters, so in the following case no parse error shall be thrown + // and no record shall be read + swc_record record, record_expected; + std::stringstream is; + is << "#comment\r"; + is << "1 1 14.566132 34.873772 7.857000 0.717830 -1\r"; + + EXPECT_NO_THROW(is >> record); + expect_record_equals(record_expected, record); + } { // check last line case (no newline at the end) @@ -233,9 +285,9 @@ TEST(swc_parser, from_allen_db) using namespace nest::mc; std::string datadir{DATADIR}; - auto fname = datadir+"/example.swc"; + auto fname = datadir + "/example.swc"; std::ifstream fid(fname); - if(!fid.is_open()) { + if (!fid.is_open()) { std::cerr << "unable to open file " << fname << "... skipping test\n"; return; } @@ -400,7 +452,7 @@ TEST(swc_record_ranges, raw) } ADD_FAILURE() << "expected an exception\n"; - } catch (const swc_parse_error &e) { + } catch (const swc_parse_error& e) { EXPECT_EQ(3u, e.lineno()); } } @@ -432,11 +484,11 @@ TEST(swc_io, cell_construction) std::stringstream is; is << "1 1 0 0 0 2.1 -1\n"; - is << "2 2 0.1 1.2 1.2 1.3 1\n"; - is << "3 2 1.0 2.0 2.2 1.1 2\n"; - is << "4 2 1.5 3.3 1.3 2.2 3\n"; - is << "5 2 2.5 5.3 2.5 0.7 3\n"; - is << "6 2 3.5 2.3 3.7 3.4 5\n"; + is << "2 3 0.1 1.2 1.2 1.3 1\n"; + is << "3 3 1.0 2.0 2.2 1.1 2\n"; + is << "4 3 1.5 3.3 1.3 2.2 3\n"; + is << "5 3 2.5 5.3 2.5 0.7 3\n"; + is << "6 3 3.5 2.3 3.7 3.4 5\n"; using point_type = point<double>; std::vector<point_type> points = { @@ -500,9 +552,9 @@ TEST(swc_io, cell_construction) TEST(swc_parser, from_file_ball_and_stick) { std::string datadir{DATADIR}; - auto fname = datadir+"/ball_and_stick.swc"; + auto fname = datadir + "/ball_and_stick.swc"; std::ifstream fid(fname); - if(!fid.is_open()) { + if (!fid.is_open()) { std::cerr << "unable to open file " << fname << "... skipping test\n"; return; }