diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 457f38aa6b2e9d6c898a759e821fdf5e06326bd9..5696287e9b2c56096146c67fe0d891548a0abfd4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(HEADERS swcio.hpp ) set(BASE_SOURCES + parameter_list.cpp swcio.cpp cell.cpp ) diff --git a/src/parameter_list.cpp b/src/parameter_list.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4394e800b32137501f3fc48b57ee573898926cad --- /dev/null +++ b/src/parameter_list.cpp @@ -0,0 +1,99 @@ +#include <algorithm> +#include <iostream> + +#include "parameter_list.hpp" + +namespace nestmc { + + bool parameter_list::add_parameter(parameter p) + { + if(has_parameter(p.name)) { + return false; + } + + parameters_.push_back(std::move(p)); + + return true; + } + + bool parameter_list::has_parameter(std::string const& n) const + { + return find_by_name(n) != parameters_.end(); + } + + int parameter_list::num_parameters() const + { + return parameters_.size(); + } + + // returns true if parameter was succesfully updated + // returns false if parameter was not updated, i.e. if + // - no parameter with name n exists + // - value is not in the valid range + bool parameter_list::set(std::string const& n, parameter_list::value_type v) + { + auto p = find_by_name(n); + if(p!=parameters_.end()) { + if(p->is_in_range(v)) { + p->value = v; + return true; + } + } + return false; + } + + parameter& parameter_list::get(std::string const& n) + { + return *find_by_name(n); + } + + std::string const& parameter_list::name() const { + return mechanism_name_; + } + + std::vector<parameter> const& parameter_list::parameters() const + { + return parameters_; + } + + auto parameter_list::find_by_name(std::string const& n) + -> decltype(parameters_.begin()) + { + return + std::find_if( + parameters_.begin(), parameters_.end(), + [&n](parameter const& p) {return p.name == n;} + ); + } + + auto parameter_list::find_by_name(std::string const& n) const + -> decltype(parameters_.begin()) + { + return + std::find_if( + parameters_.begin(), parameters_.end(), + [&n](parameter const& p) {return p.name == n;} + ); + } + + + std::ostream& operator<<(std::ostream& o, parameter const& p) + { + return o + << "parameter(" + << "name " << p.name + << " : value " << p.value + << " : range " << p.range + << ")"; + } + + std::ostream& operator<<(std::ostream& o, parameter_list const& l) + { + o << "parameters \"" << l.name() << "\" :\n"; + for(auto const& p : l.parameters()) { + o << " " << p << "\n"; + } + return o; + } + +} // namespace nestmc diff --git a/src/parameter_list.hpp b/src/parameter_list.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ef790a640d40264585c1dcdb25c9d917a72624bb --- /dev/null +++ b/src/parameter_list.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include <limits> +#include <ostream> +#include <stdexcept> +#include <string> +#include <vector> + +namespace nestmc { + + template <typename T> + struct value_range { + using value_type = T; + + value_range() + : min(std::numeric_limits<value_type>::quiet_NaN()), + max(std::numeric_limits<value_type>::quiet_NaN()) + { } + + value_range(value_type const& left, value_type const& right) + : min(left), + max(right) + { + if(left>right) { + throw std::out_of_range( + "parameter range must have left <= right" + ); + } + } + + bool has_lower_bound() const + { + return min==min; + } + + bool has_upper_bound() const + { + return max==max; + } + + bool is_in_range(value_type const& v) const + { + if(has_lower_bound()) { + if(v<min) return false; + } + if(has_upper_bound()) { + if(v>max) return false; + } + return true; + } + + value_type min; + value_type max; + }; + + template <typename T> + std::ostream& operator<<(std::ostream& o, value_range<T> const& r) + { + return + o << "[ " + << (r.has_lower_bound() ? std::to_string(r.min) : "-inf") << ", " + << (r.has_upper_bound() ? std::to_string(r.max) : "inf") << "]"; + } + + struct parameter { + using value_type = double; + using range_type = value_range<value_type>; + + parameter() = delete; + + parameter(std::string n, value_type v, range_type r=range_type()) + : name(std::move(n)), + value(v), + range(r) + { + if(!is_in_range(v)) { + throw std::out_of_range( + "parameter value is out of permitted value range" + ); + } + } + + bool is_in_range(value_type v) const + { + return range.is_in_range(v); + } + + std::string name; + value_type value; + range_type range; + }; + + std::ostream& operator<<(std::ostream& o, parameter const& p); + + // Use a dumb container class for now + // might have to use a more sophisticated interface in the future if need be + class parameter_list { + public : + + using value_type = double; + + parameter_list(std::string n) + : mechanism_name_(std::move(n)) + { } + + bool has_parameter(std::string const& n) const; + bool add_parameter(parameter p); + + // returns true if parameter was succesfully updated + // returns false if parameter was not updated, i.e. if + // - no parameter with name n exists + // - value is not in the valid range + bool set(std::string const& n, value_type v); + parameter& get(std::string const& n); + + std::string const& name() const; + + std::vector<parameter> const& parameters() const; + + int num_parameters() const; + + private: + + std::vector<parameter> parameters_; + std::string mechanism_name_; + + // need two versions for const and non-const applications + auto find_by_name(std::string const& n) + -> decltype(parameters_.begin()); + + auto find_by_name(std::string const& n) const + -> decltype(parameters_.begin()); + + }; + + std::ostream& operator<<(std::ostream& o, parameter_list const& l); + + class hh_parameters + : public parameter_list + { + public: + + using base = parameter_list; + + using base::value_type; + + using base::set; + using base::get; + using base::parameters; + using base::has_parameter; + + hh_parameters() + : base("hh") + { + base::add_parameter({"gnabar", 0.12, {0., 1e9}}); + base::add_parameter({"gkbar", 0.036, {0., 1e9}}); + base::add_parameter({"gl", 0.0003,{0., 1e9}}); + base::add_parameter({"el", -54.3}); + } + }; + +} // namespace nestmc + diff --git a/src/segment.hpp b/src/segment.hpp index a2429032f28d97aa61f87eb31f79b9783eca4246..ca7251b898ec8800ec71711777bcd44863e618f7 100644 --- a/src/segment.hpp +++ b/src/segment.hpp @@ -5,6 +5,7 @@ #include <vector> #include "math.hpp" +#include "parameter_list.hpp" #include "point.hpp" #include "util.hpp" @@ -76,9 +77,40 @@ class segment { segment_properties<value_type> properties; + void add_mechanism(parameter_list p) + { + auto it = std::find_if( + mechanisms_.begin(), mechanisms_.end(), + [&p](parameter_list const& l){return l.name() == p.name();} + ); + if(it!=mechanisms_.end()) { + throw std::out_of_range( + "attempt to add a mechanism parameter set to a segment which has an existing mechanism with the same name" + ); + } + + mechanisms_.push_back(std::move(p)); + } + + parameter_list& mechanism(std::string const& n) + { + auto it = std::find_if( + mechanisms_.begin(), mechanisms_.end(), + [&n](parameter_list const& l){return l.name() == n;} + ); + if(it==mechanisms_.end()) { + throw std::out_of_range( + "attempt to access a parameter that is not defined in a segment" + ); + } + + return *it; + } + protected: segmentKind kind_; + std::vector<parameter_list> mechanisms_; }; class placeholder_segment : public segment diff --git a/tests/test_run.cpp b/tests/test_run.cpp index f80cb41f612d2fd53db9162ec6b8400634cf91bc..618d9e79560ea0764d7c22ec844749dc8ec51ffa 100644 --- a/tests/test_run.cpp +++ b/tests/test_run.cpp @@ -15,25 +15,69 @@ TEST(run, init) EXPECT_EQ(cell.graph().num_segments(), 2u); + /* for(auto &s : cell.segments()) { std::cout << "volume : " << s->volume() << " area : " << s->area() << " ratio : " << s->volume()/s->area() << std::endl; } - -#ifdef example_1 + */ // in this context (i.e. attached to a segment on a high-level cell) // a mechanism is essentially a set of parameters // - the only "state" is that used to define parameters - cell.soma()->add_mechanism("hh"); + cell.soma()->add_mechanism(hh_parameters()); + + auto& soma_hh = cell.soma()->mechanism("hh"); - auto& soma_hh = cell.soma()->mechanisms("hh"); soma_hh.set("gnabar", 0.12); soma_hh.set("gkbar", 0.036); soma_hh.set("gl", 0.0003); soma_hh.set("el", -54.3); - fvm_cell fv(cell); -#endif + // check that parameter values were set correctly + EXPECT_EQ(cell.soma()->mechanism("hh").get("gnabar").value, 0.12); + EXPECT_EQ(cell.soma()->mechanism("hh").get("gkbar").value, 0.036); + EXPECT_EQ(cell.soma()->mechanism("hh").get("gl").value, 0.0003); + EXPECT_EQ(cell.soma()->mechanism("hh").get("el").value, -54.3); + + // print out the parameters if you want... + //std::cout << soma_hh << "\n"; + +} + +// test out the parameter infrastructure +TEST(run, parameters) +{ + nestmc::parameter_list list("test"); + EXPECT_EQ(list.name(), "test"); + EXPECT_EQ(list.num_parameters(), 0); + + nestmc::parameter p("a", 0.12, {0, 10}); + + // add_parameter() returns a bool that indicates whether + // it was able to successfull add the parameter + EXPECT_EQ(list.add_parameter(std::move(p)), true); + EXPECT_EQ(list.num_parameters(), 1u); + + // test in place construction of a parameter + EXPECT_EQ(list.add_parameter({"b", -3.0}), true); + EXPECT_EQ(list.num_parameters(), 2u); + + // check that adding a parameter that already exists returns false + // and does not increase the number of parameters + EXPECT_EQ(list.add_parameter({"b", -3.0}), false); + EXPECT_EQ(list.num_parameters(), 2u); + + auto &parms = list.parameters(); + EXPECT_EQ(parms[0].name, "a"); + EXPECT_EQ(parms[0].value, 0.12); + EXPECT_EQ(parms[0].range.min, 0); + EXPECT_EQ(parms[0].range.max, 10); + + EXPECT_EQ(parms[1].name, "b"); + EXPECT_EQ(parms[1].value, -3); + EXPECT_EQ(parms[1].range.has_lower_bound(), false); + EXPECT_EQ(parms[1].range.has_upper_bound(), false); } +