-
Benjamin Cumming authored68af1047
cell.cpp 5.71 KiB
#include "cell.hpp"
#include "tree.hpp"
namespace nest {
namespace mc {
int find_compartment_index(
segment_location const& location,
compartment_model const& graph
) {
EXPECTS(location.segment<graph.segment_index.size());
const auto& si = graph.segment_index;
const auto seg = location.segment;
auto first = si[seg];
auto n = si[seg+1] - first;
auto index = std::floor(n*location.position);
return index<n ? first+index : first+n-1;
}
cell::cell()
{
// insert a placeholder segment for the soma
segments_.push_back(make_segment<placeholder_segment>());
parents_.push_back(0);
}
int cell::num_segments() const
{
return segments_.size();
}
//
// note: I think that we have to enforce that the soma is the first
// segment that is added
//
soma_segment* cell::add_soma(value_type radius, point_type center)
{
if(has_soma()) {
throw std::domain_error(
"attempt to add a soma to a cell that already has one"
);
}
// add segment for the soma
if(center.is_set()) {
segments_[0] = make_segment<soma_segment>(radius, center);
}
else {
segments_[0] = make_segment<soma_segment>(radius);
}
return segments_[0]->as_soma();
}
cable_segment* cell::add_cable(cell::index_type parent, segment_ptr&& cable)
{
// check for a valid parent id
if(cable->is_soma()) {
throw std::domain_error(
"attempt to add a soma as a segment"
);
}
// check for a valid parent id
if(parent>num_segments()) {
throw std::out_of_range(
"parent index of cell segment is out of range"
);
}
segments_.push_back(std::move(cable));
parents_.push_back(parent);
return segments_.back()->as_cable();
}
segment* cell::segment(int index)
{
if(index<0 || index>=num_segments()) {
throw std::out_of_range(
"attempt to access a segment with invalid index"
);
}
return segments_[index].get();
}
segment const* cell::segment(int index) const
{
if(index<0 || index>=num_segments()) {
throw std::out_of_range(
"attempt to access a segment with invalid index"
);
}
return segments_[index].get();
}
bool cell::has_soma() const
{
return !segment(0)->is_placeholder();
}
soma_segment* cell::soma()
{
if(has_soma()) {
return segment(0)->as_soma();
}
return nullptr;
}
cable_segment* cell::cable(int index)
{
if(index>0 && index<num_segments()) {
return segment(index)->as_cable();
}
return nullptr;
}
cell::value_type cell::volume() const
{
return
std::accumulate(
segments_.begin(), segments_.end(),
0.,
[](double value, segment_ptr const& seg) {
return seg->volume() + value;
}
);
}
cell::value_type cell::area() const
{
return
std::accumulate(
segments_.begin(), segments_.end(),
0.,
[](double value, segment_ptr const& seg) {
return seg->area() + value;
}
);
}
std::vector<segment_ptr> const& cell::segments() const
{
return segments_;
}
std::vector<int> cell::compartment_counts() const
{
std::vector<int> comp_count;
comp_count.reserve(num_segments());
for(auto const& s : segments()) {
comp_count.push_back(s->num_compartments());
}
return comp_count;
}
size_t cell::num_compartments() const
{
auto n = 0u;
for(auto &s : segments_) {
n += s->num_compartments();
}
return n;
}
compartment_model cell::model() const
{
compartment_model m;
m.tree = cell_tree(parents_);
auto counts = compartment_counts();
m.parent_index = make_parent_index(m.tree.graph(), counts);
m.segment_index = algorithms::make_index(counts);
return m;
}
void cell::add_stimulus(segment_location loc, i_clamp stim)
{
if(!(loc.segment<num_segments())) {
throw std::out_of_range(
util::pprintf(
"can't insert stimulus in segment % of a cell with % segments",
loc.segment, num_segments()
)
);
}
stimulii_.push_back({loc, std::move(stim)});
}
void cell::add_detector(segment_location loc, double threshold)
{
spike_detectors_.push_back({loc, threshold});
}
std::vector<int> const& cell::segment_parents() const
{
return parents_;
}
void cell::add_synapse(segment_location loc)
{
synapses_.push_back(loc);
}
const std::vector<segment_location>& cell::synapses() const
{
return synapses_;
}
// 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