diff --git a/arbor/cable_cell.cpp b/arbor/cable_cell.cpp index 90dcd74438148e6adf4d9cd262af4ea004303a21..55bdb9392f483743c30256274e8d4fd7f61b7246 100644 --- a/arbor/cable_cell.cpp +++ b/arbor/cable_cell.cpp @@ -17,20 +17,117 @@ using value_type = cable_cell::value_type; using index_type = cable_cell::index_type; using size_type = cable_cell::size_type; -cable_cell::cable_cell() { - // insert a placeholder segment for the soma - segments_.push_back(make_segment<placeholder_segment>()); - parents_.push_back(0); -} +struct cable_cell_impl { + using value_type = cable_cell::value_type; + using index_type = cable_cell::index_type; + using size_type = cable_cell::size_type; + + using stimulus_instance = cable_cell::stimulus_instance; + using synapse_instance = cable_cell::synapse_instance; + using gap_junction_instance = cable_cell::gap_junction_instance; + using detector_instance = cable_cell::detector_instance; + + using region_map = cable_cell::region_map; + using locset_map = cable_cell::locset_map; + + cable_cell_impl() { + segments.push_back(make_segment<placeholder_segment>()); + parents.push_back(0); + } -void cable_cell::assert_valid_segment(index_type i) const { - if (i>=num_segments()) { - throw cable_cell_error("no such segment"); + cable_cell_impl(const cable_cell_impl& other) { + parents = other.parents; + stimuli = other.stimuli; + synapses = other.synapses; + gap_junction_sites = other.gap_junction_sites; + spike_detectors = other.spike_detectors; + regions = other.regions; + morph = other.morph; + locations = other.locations; + + // unique_ptr's cannot be copy constructed, do a manual assignment + segments.reserve(other.segments.size()); + for (const auto& s: other.segments) { + segments.push_back(s->clone()); + } } + + cable_cell_impl(cable_cell_impl&& other) = default; + + // storage for connections + std::vector<index_type> parents; + + // the segments + std::vector<segment_ptr> segments; + + // the stimuli + std::vector<stimulus_instance> stimuli; + + // the synapses + std::vector<synapse_instance> synapses; + + // the gap_junctions + std::vector<gap_junction_instance> gap_junction_sites; + + // the sensors + std::vector<detector_instance> spike_detectors; + + // Named regions + region_map regions; + + // Named location sets + locset_map locations; + + // Underlying embedded morphology + em_morphology morph; + + template <typename Desc, typename T> + lid_range place(const mlocation_list& locs, const Desc& desc, std::vector<T>& list) { + const auto first = list.size(); + + list.reserve(first+locs.size()); + for (auto loc: locs) { + list.push_back({loc, desc}); + } + + return lid_range(first, list.size()); + } + + lid_range place_gj(const mlocation_list& locs) { + const auto first = gap_junction_sites.size(); + + gap_junction_sites.insert(gap_junction_sites.end(), locs.begin(), locs.end()); + + return lid_range(first, gap_junction_sites.size()); + } + + void assert_valid_segment(index_type i) const { + if (i>=segments.size()) { + throw cable_cell_error("no such segment"); + } + } + + bool valid_location(const mlocation& loc) const { + return test_invariants(loc) && loc.branch<segments.size(); + } +}; + +using impl_ptr = std::unique_ptr<cable_cell_impl, void (*)(cable_cell_impl*)>; +impl_ptr make_impl(cable_cell_impl* c) { + return impl_ptr(c, [](cable_cell_impl* p){delete p;}); } +cable_cell::cable_cell(): + impl_(make_impl(new cable_cell_impl())) +{} + +cable_cell::cable_cell(const cable_cell& other): + default_parameters(other.default_parameters), + impl_(make_impl(new cable_cell_impl(*other.impl_))) +{} + size_type cable_cell::num_segments() const { - return segments_.size(); + return impl_->segments.size(); } // @@ -41,8 +138,8 @@ soma_segment* cable_cell::add_soma(value_type radius, point_type center) { if (has_soma()) { throw cable_cell_error("cell already has soma"); } - segments_[0] = make_segment<soma_segment>(radius, center); - return segments_[0]->as_soma(); + impl_->segments[0] = make_segment<soma_segment>(radius, center); + return impl_->segments[0]->as_soma(); } cable_segment* cable_cell::add_cable(index_type parent, segment_ptr&& cable) { @@ -54,41 +151,210 @@ cable_segment* cable_cell::add_cable(index_type parent, segment_ptr&& cable) { throw cable_cell_error("parent index out of range"); } - segments_.push_back(std::move(cable)); - parents_.push_back(parent); + impl_->segments.push_back(std::move(cable)); + impl_->parents.push_back(parent); - return segments_.back()->as_cable(); + return impl_->segments.back()->as_cable(); } segment* cable_cell::segment(index_type index) { - assert_valid_segment(index); - return segments_[index].get(); + impl_->assert_valid_segment(index); + return impl_->segments[index].get(); } + segment const* cable_cell::parent(index_type index) const { - assert_valid_segment(index); - return segments_[parents_[index]].get(); + impl_->assert_valid_segment(index); + return impl_->segments[impl_->parents[index]].get(); } segment const* cable_cell::segment(index_type index) const { - assert_valid_segment(index); - return segments_[index].get(); + impl_->assert_valid_segment(index); + return impl_->segments[index].get(); +} + +const std::vector<segment_ptr>& cable_cell::segments() const { + return impl_->segments; +} + +const std::vector<index_type>& cable_cell::parents() const { + return impl_->parents; +} + +value_type cable_cell::segment_length_constant(value_type frequency, index_type segidx, + const cable_cell_parameter_set& global_defaults) const +{ + return 0.5/segment_mean_attenuation(frequency, segidx, global_defaults); } bool cable_cell::has_soma() const { return !segment(0)->is_placeholder(); } -void cable_cell::paint(const std::string& target, const std::string& description) { - auto it = regions_.find(target); +const std::vector<cable_cell::gap_junction_instance>& cable_cell::gap_junction_sites() const { + return impl_->gap_junction_sites; +} + +const std::vector<cable_cell::synapse_instance>& cable_cell::synapses() const { + return impl_->synapses; +} + +const std::vector<cable_cell::detector_instance>& cable_cell::detectors() const { + return impl_->spike_detectors; +} + +const std::vector<cable_cell::stimulus_instance>& cable_cell::stimuli() const { + return impl_->stimuli; +} + +void cable_cell::set_regions(cable_cell::region_map r) { + impl_->regions = std::move(r); +} + +void cable_cell::set_locsets(cable_cell::locset_map l) { + impl_->locations = std::move(l); +} + +// +// Painters. +// +// Implementation of user API for painting density channel and electrical properties on cells. +// + +void cable_cell::paint(const std::string& target, mechanism_desc desc) { + auto it = impl_->regions.find(target); // Nothing to do if there are no regions that match. - if (it==regions_.end()) return; + if (it==impl_->regions.end()) return; + + for (auto c: it->second) { + if (c.prox_pos!=0 || c.dist_pos!=1) { + throw cable_cell_error(util::pprintf( + "cable_cell does not support regions with partial branches: \"{}\": {}", + target, c)); + } + segment(c.branch)->add_mechanism(std::move(desc)); + } +} + +// +// Placers. +// +// Implementation of user API for placing discrete items on cell morphology, +// such as synapses, spike detectors and stimuli. +// + +// +// Synapses +// + +lid_range cable_cell::place(const std::string& target, const mechanism_desc& desc) { + const auto first = impl_->synapses.size(); + + const auto it = impl_->locations.find(target); + if (it==impl_->locations.end()) return lid_range(first, first); + + return impl_->place(it->second, desc, impl_->synapses); +} + +/* +lid_range cable_cell::place(const locset& ls, const mechanism_desc& desc) { + const auto locs = thingify(ls, impl_->morph); + return impl_->place(locs, desc, impl_->synapses); +} +*/ + +lid_range cable_cell::place(const mlocation& loc, const mechanism_desc& desc) { + if (!impl_->valid_location(loc)) { + throw cable_cell_error(util::pprintf( + "Attempt to add synapse at invalid location: \"{}\"", loc)); + } + return impl_->place({loc}, desc, impl_->synapses); +} + +// +// Stimuli +// + +lid_range cable_cell::place(const std::string& target, const i_clamp& desc) { + const auto first = impl_->stimuli.size(); + + const auto it = impl_->locations.find(target); + if (it==impl_->locations.end()) return lid_range(first, first); + + return impl_->place(it->second, desc, impl_->stimuli); +} + +/* +lid_range cable_cell::place(const locset& ls, const i_clamp& desc) { + const auto locs = thingify(ls, impl_->morph); + return impl_->place(locs, desc, impl_->stimuli); +} +*/ + +lid_range cable_cell::place(const mlocation& loc, const i_clamp& desc) { + if (!impl_->valid_location(loc)) { + throw cable_cell_error(util::pprintf( + "Attempt to add stimulus at invalid location: {}", loc)); + } + return impl_->place({loc}, desc, impl_->stimuli); +} - for (auto i: it->second) { - segment(i)->add_mechanism(description); +// +// Gap junctions. +// + +lid_range cable_cell::place(const std::string& target, gap_junction_site) { + const auto first = impl_->stimuli.size(); + + const auto it = impl_->locations.find(target); + if (it==impl_->locations.end()) return lid_range(first, first); + + return impl_->place_gj(it->second); +} + +/* +lid_range cable_cell::place(const locset& ls, gap_junction_site) { + const auto locs = thingify(ls, impl_->morph); + return impl_->place_gj(locs); +} +*/ + +lid_range cable_cell::place(const mlocation& loc, gap_junction_site) { + if (!impl_->valid_location(loc)) { + throw cable_cell_error(util::pprintf( + "Attempt to add gap junction site at invalid location: {}", loc)); } + return impl_->place_gj({loc}); +} + +// +// Spike detectors. +// +lid_range cable_cell::place(const std::string& target, const threshold_detector& desc) { + const auto first = impl_->stimuli.size(); + + const auto it = impl_->locations.find(target); + if (it==impl_->locations.end()) return lid_range(first, first); + + return impl_->place(it->second, desc.threshold, impl_->spike_detectors); } +/* +lid_range cable_cell::place(const locset& ls, const threshold_detector& desc) { + const auto locs = thingify(ls, impl_->morph); + return impl_->place(locs, desc.threshold, impl_->spike_detectors); +} +*/ + +lid_range cable_cell::place(const mlocation& loc, const threshold_detector& desc) { + if (!impl_->valid_location(loc)) { + throw cable_cell_error(util::pprintf( + "Attempt to add spike detector at invalid location: {}", loc)); + } + return impl_->place({loc}, desc.threshold, impl_->spike_detectors); +} + + soma_segment* cable_cell::soma() { return has_soma()? segment(0)->as_soma(): nullptr; } @@ -98,13 +364,13 @@ const soma_segment* cable_cell::soma() const { } cable_segment* cable_cell::cable(index_type index) { - assert_valid_segment(index); + impl_->assert_valid_segment(index); auto cable = segment(index)->as_cable(); return cable? cable: throw cable_cell_error("segment is not a cable segment"); } const cable_segment* cable_cell::cable(index_type index) const { - assert_valid_segment(index); + impl_->assert_valid_segment(index); auto cable = segment(index)->as_cable(); return cable? cable: throw cable_cell_error("segment is not a cable segment"); } @@ -118,18 +384,17 @@ std::vector<size_type> cable_cell::compartment_counts() const { return comp_count; } -size_type cable_cell::num_compartments() const { - return util::sum_by(segments_, - [](const segment_ptr& s) { return s->num_compartments(); }); +const em_morphology* cable_cell::morphology() const { + return &(impl_->morph); } -void cable_cell::add_stimulus(mlocation loc, i_clamp stim) { - (void)segment(loc.branch); // assert loc.segment in range - stimuli_.push_back({loc, std::move(stim)}); +void cable_cell::set_morphology(em_morphology m) { + impl_->morph = std::move(m); } -void cable_cell::add_detector(mlocation loc, double threshold) { - spike_detectors_.push_back({loc, threshold}); +size_type cable_cell::num_compartments() const { + return util::sum_by(impl_->segments, + [](const segment_ptr& s) { return s->num_compartments(); }); } // Approximating wildly by ignoring O(x) effects entirely, the attenuation b @@ -145,8 +410,10 @@ value_type cable_cell::segment_mean_attenuation( value_type frequency, index_type segidx, const cable_cell_parameter_set& global_defaults) const { - value_type R = default_parameters.axial_resistivity.value_or(global_defaults.axial_resistivity.value()); - value_type C = default_parameters.membrane_capacitance.value_or(global_defaults.membrane_capacitance.value()); + value_type R = default_parameters.axial_resistivity.value_or( + global_defaults.axial_resistivity.value()); + value_type C = default_parameters.membrane_capacitance.value_or( + global_defaults.membrane_capacitance.value()); value_type length_factor = 0; // [1/√µm] @@ -186,10 +453,10 @@ cable_cell make_cable_cell(const morphology& m, bool compartments_from_discretization) { using point3d = cable_cell::point_type; - cable_cell newcell; + cable_cell cell; if (!m.num_branches()) { - return newcell; + return cell; } // Add the soma. @@ -197,7 +464,7 @@ cable_cell make_cable_cell(const morphology& m, // If there is no spherical root/soma use a zero-radius soma. double srad = m.spherical_root()? loc.radius: 0.; - newcell.add_soma(srad, point3d(loc.x, loc.y, loc.z)); + cell.add_soma(srad, point3d(loc.x, loc.y, loc.z)); auto& samples = m.samples(); for (auto i: util::make_span(1, m.num_branches())) { @@ -232,7 +499,7 @@ cable_cell make_cable_cell(const morphology& m, if (!m.spherical_root()) { pid = pid==mnpos? 0: pid+1; } - auto cable = newcell.add_cable(pid, kind, radii, points); + auto cable = cell.add_cable(pid, make_segment<cable_segment>(kind, radii, points)); if (compartments_from_discretization) { cable->as_cable()->set_compartments(radii.size()-1); } @@ -242,23 +509,21 @@ cable_cell make_cable_cell(const morphology& m, // Ignores the pointsets, for now. auto em = em_morphology(m); // for converting labels to "segments" - std::unordered_map<std::string, std::vector<msize_t>> regions; + std::unordered_map<std::string, mcable_list> regions; for (auto r: dictionary.regions()) { - mcable_list L = thingify(r.second, em); - std::vector<msize_t> bids; - for (auto c: L) { - if (c.prox_pos!=0 || c.dist_pos!=1) { - throw cable_cell_error(util::pprintf( - "cable_cell does not support regions with partial branches: \"{}\": {}", - r.first, c)); - } - bids.push_back(c.branch); - } - regions[r.first] = bids; + regions[r.first] = thingify(r.second, em); } - newcell.set_regions(std::move(regions)); + cell.set_regions(std::move(regions)); + + std::unordered_map<std::string, mlocation_list> locsets; + for (auto l: dictionary.locsets()) { + locsets[l.first] = thingify(l.second, em); + } + cell.set_locsets(std::move(locsets)); + + cell.set_morphology(std::move(em)); - return newcell; + return cell; } } // namespace arb diff --git a/arbor/include/arbor/cable_cell.hpp b/arbor/include/arbor/cable_cell.hpp index 2303aaff7f63e70277b005b0c38e7fd1f9c307ea..2e35ded6c276c9a441c756b71b1134cc26d3b034 100644 --- a/arbor/include/arbor/cable_cell.hpp +++ b/arbor/include/arbor/cable_cell.hpp @@ -1,5 +1,6 @@ #pragma once + #include <unordered_map> #include <string> #include <vector> @@ -16,22 +17,17 @@ namespace arb { -// Current clamp description for stimulus specification. - -struct i_clamp { - using value_type = double; - - value_type delay = 0; // [ms] - value_type duration = 0; // [ms] - value_type amplitude = 0; // [nA] - - i_clamp(value_type delay, value_type duration, value_type amplitude): - delay(delay), duration(duration), amplitude(amplitude) - {} +// Pair of indexes that describe range of local indices. +// Returned by cable_cell::place() calls, so that the caller can +// refer to targets, detectors, etc on the cell. +struct lid_range { + cell_lid_type begin; + cell_lid_type end; + lid_range(cell_lid_type b, cell_lid_type e): + begin(b), end(e) {} }; // Probe type for cell descriptions. - struct cell_probe_address { enum probe_kind { membrane_voltage, membrane_current @@ -41,7 +37,10 @@ struct cell_probe_address { probe_kind kind; }; -/// high-level abstract representation of a cell and its segments +// Forward declare the implementation, for PIMPL. +struct cable_cell_impl; + +// High-level abstract representation of a cell and its segments class cable_cell { public: using index_type = cell_lid_type; @@ -49,9 +48,10 @@ public: using value_type = double; using point_type = point<value_type>; - using gap_junction_instance = mlocation; + using region_map = std::unordered_map<std::string, mcable_list>; + using locset_map = std::unordered_map<std::string, mlocation_list>; - using region_map = std::unordered_map<std::string, std::vector<msize_t>>; + using gap_junction_instance = mlocation; struct synapse_instance { mlocation location; @@ -74,30 +74,11 @@ public: cable_cell(); /// Copy constructor - cable_cell(const cable_cell& other): - default_parameters(other.default_parameters), - parents_(other.parents_), - stimuli_(other.stimuli_), - synapses_(other.synapses_), - gap_junction_sites_(other.gap_junction_sites_), - spike_detectors_(other.spike_detectors_), - regions_(other.regions_) - { - // unique_ptr's cannot be copy constructed, do a manual assignment - segments_.reserve(other.segments_.size()); - for (const auto& s: other.segments_) { - segments_.push_back(s->clone()); - } - } + cable_cell(const cable_cell& other); /// Move constructor cable_cell(cable_cell&& other) = default; - /// Return the kind of cell, used for grouping into cell_groups - cell_kind get_cell_kind() const { - return cell_kind::cable; - } - /// add a soma to the cell /// radius must be specified soma_segment* add_soma(value_type radius, point_type center=point_type()); @@ -107,102 +88,85 @@ public: /// cable is the segment that will be moved into the cell cable_segment* add_cable(index_type parent, segment_ptr&& cable); - /// add a cable by constructing it in place - /// parent is the index of the parent segment for the cable section - /// args are the arguments to be used to consruct the new cable - template <typename... Args> - cable_segment* add_cable(index_type parent, Args&&... args); - - /// the number of segments in the cell - size_type num_segments() const; - bool has_soma() const; class segment* segment(index_type index); const class segment* parent(index_type index) const; const class segment* segment(index_type index) const; - /// access pointer to the soma - /// returns nullptr if the cell has no soma + // access pointer to the soma + // returns nullptr if the cell has no soma + // LEGACY soma_segment* soma(); const soma_segment* soma() const; - /// access pointer to a cable segment - /// will throw an cable_cell_error exception if - /// the cable index is not valid + // access pointer to a cable segment + // will throw an cable_cell_error exception if + // the cable index is not valid + // LEGACY cable_segment* cable(index_type index); const cable_segment* cable(index_type index) const; - /// the total number of compartments over all segments - size_type num_compartments() const; + const std::vector<segment_ptr>& segments() const; - std::vector<segment_ptr> const& segments() const { - return segments_; - } + // the number of segments in the cell + size_type num_segments() const; - /// return a vector with the compartment count for each segment in the cell + // return a vector with the compartment count for each segment in the cell + // LEGACY std::vector<size_type> compartment_counts() const; - ////////////////// - // stimuli - ////////////////// - void add_stimulus(mlocation loc, i_clamp stim); - - std::vector<stimulus_instance>& - stimuli() { - return stimuli_; - } - - const std::vector<stimulus_instance>& - stimuli() const { - return stimuli_; - } - - ////////////////// - // painters - ////////////////// - void paint(const std::string& target, const std::string& description); - - ////////////////// - // synapses - ////////////////// - void add_synapse(mlocation loc, mechanism_desc p) - { - synapses_.push_back(synapse_instance{loc, std::move(p)}); - } - const std::vector<synapse_instance>& synapses() const { - return synapses_; - } - - ////////////////// - // gap-junction - ////////////////// - void add_gap_junction(mlocation location) - { - gap_junction_sites_.push_back(location); - } - const std::vector<gap_junction_instance>& gap_junction_sites() const { - return gap_junction_sites_; - } - - ////////////////// + // The total number of compartments in the discretised cell. + // LEGACY + size_type num_compartments() const; + + // + // Painters and placers. + // + // Used to describe regions and locations where density channels, stimuli, + // synapses, gap juncitons and detectors are located. + // + + // Density channels. + void paint(const std::string& target, mechanism_desc); + + // Synapses. + lid_range place(const std::string& target, const mechanism_desc&); + lid_range place(const mlocation&, const mechanism_desc&); // LEGACY + //lid_range place(const locset&, const mechanism_desc&); + + // Stimuli. + lid_range place(const std::string& target, const i_clamp&); + lid_range place(const mlocation&, const i_clamp&); // LEGACY + //lid_range place(const locset&, const i_clamp&); + + // Gap junctions. + lid_range place(const std::string&, gap_junction_site); + lid_range place(const mlocation& loc, gap_junction_site); // LEGACY + //lid_range place(const locset&, gap_junction_site); + // spike detectors - ////////////////// - void add_detector(mlocation loc, double threshold); + lid_range place(const std::string&, const threshold_detector&); + lid_range place(const mlocation&, const threshold_detector&); // LEGACY + //lid_range place(const locset&, const threshold_detector&); + + // + // access to placed items + // - std::vector<detector_instance>& - detectors() { - return spike_detectors_; - } + const std::vector<synapse_instance>& synapses() const; + const std::vector<gap_junction_instance>& gap_junction_sites() const; + const std::vector<detector_instance>& detectors() const; + const std::vector<stimulus_instance>& stimuli() const; - const std::vector<detector_instance>& - detectors() const { - return spike_detectors_; - } + // These setters are temporary, for "side-loading" in make_cable_cell. + // In the regions, locset and morphology descriptions will be passed directly + // to the cable_cell constructor. + void set_regions(region_map r); + void set_locsets(locset_map l); + void set_morphology(em_morphology m); - void set_regions(region_map r) { - regions_ = std::move(r); - } + const em_morphology* morphology() const; // Checks that two cells have the same // - number and type of segments @@ -212,9 +176,7 @@ public: friend bool cell_basic_equality(const cable_cell&, const cable_cell&); // Public view of parent indices vector. - const std::vector<index_type>& parents() const { - return parents_; - } + const std::vector<index_type>& parents() const; // Approximate per-segment mean attenuation b(f) at given frequency f, // ignoring membrane resistance [1/µm]. @@ -225,50 +187,13 @@ public: // Hines and Carnevale (2001), "NEURON: A Tool for Neuroscientists", // Neuroscientist 7, pp. 123-135. value_type segment_length_constant(value_type frequency, index_type segidx, - const cable_cell_parameter_set& global_defaults) const - { - return 0.5/segment_mean_attenuation(frequency, segidx, global_defaults); - } + const cable_cell_parameter_set& global_defaults) const; private: - void assert_valid_segment(index_type) const; - - // storage for connections - std::vector<index_type> parents_; - - // the segments - std::vector<segment_ptr> segments_; - - // the stimuli - std::vector<stimulus_instance> stimuli_; - // the synapses - std::vector<synapse_instance> synapses_; - - // the gap_junctions - std::vector<gap_junction_instance> gap_junction_sites_; - - // the sensors - std::vector<detector_instance> spike_detectors_; - - // Named regions, oh my. - region_map regions_; + std::unique_ptr<cable_cell_impl, void (*)(cable_cell_impl*)> impl_; }; -// create a cable by forwarding cable construction parameters provided by the user -template <typename... Args> -cable_segment* cable_cell::add_cable(cable_cell::index_type parent, Args&&... args) -{ - // check for a valid parent id - if (parent>=num_segments()) { - throw cable_cell_error("parent index of cell segment is out of range"); - } - segments_.push_back(make_segment<cable_segment>(std::forward<Args>(args)...)); - parents_.push_back(parent); - - return segments_.back()->as_cable(); -} - // Create a cable cell from a morphology specification. // If compartments_from_discretization is true, set number of compartments // in each segment to be the number of piecewise linear sections in the diff --git a/arbor/include/arbor/cable_cell_param.hpp b/arbor/include/arbor/cable_cell_param.hpp index 2c147c0ce25667eb91ee1b0ee12e285e4cbfc754..8ef752abae28868b00d2feecb11ebd065d61e77e 100644 --- a/arbor/include/arbor/cable_cell_param.hpp +++ b/arbor/include/arbor/cable_cell_param.hpp @@ -16,6 +16,30 @@ struct cable_cell_error: arbor_exception { arbor_exception("cable_cell: "+what) {} }; +// Current clamp description for stimulus specification. +struct i_clamp { + using value_type = double; + + value_type delay = 0; // [ms] + value_type duration = 0; // [ms] + value_type amplitude = 0; // [nA] + + i_clamp() = default; + + i_clamp(value_type delay, value_type duration, value_type amplitude): + delay(delay), duration(duration), amplitude(amplitude) + {} +}; + +// Threshold detector description. +struct threshold_detector { + double threshold; +}; + +// Tag type for dispatching cable_cell::place() calls that add gap junction sites. +struct gap_junction_site {}; + + // Mechanism description, viz. mechanism name and // (non-global) parameter settings. Used to assign // density and point mechanisms to segments and diff --git a/arbor/include/arbor/morph/locset.hpp b/arbor/include/arbor/morph/locset.hpp index 00c7e97386025aa9dbbeea782bb0ed178e01733a..63e0e6923e1fc4ecf772cb6c7de0c53f43e1eb81 100644 --- a/arbor/include/arbor/morph/locset.hpp +++ b/arbor/include/arbor/morph/locset.hpp @@ -17,10 +17,10 @@ namespace arb { // interface for concretising locsets. class em_morphology; +class locset; + class locset { public: - locset() = delete; - template <typename Impl, typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, locset>::value>> explicit locset(Impl&& impl): @@ -40,6 +40,12 @@ public: return *this; } + // The default constructor creates an empty "nil" set. + locset(); + + // Construct an explicit location set with a single location. + locset(mlocation other); + template <typename Impl, typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, locset>::value>> locset& operator=(Impl&& other) { diff --git a/arbor/include/arbor/morph/morphology.hpp b/arbor/include/arbor/morph/morphology.hpp index 0c9ed9b0e6726ee811fa3579c8fefc198f0f7329..16d41ec763479797434a39f533e6b29ec8afcc68 100644 --- a/arbor/include/arbor/morph/morphology.hpp +++ b/arbor/include/arbor/morph/morphology.hpp @@ -21,6 +21,7 @@ class morphology { public: morphology(sample_tree m, bool use_spherical_root); morphology(sample_tree m); + morphology(); // Whether the root of the morphology is spherical. bool spherical_root() const; diff --git a/arbor/include/arbor/morph/region.hpp b/arbor/include/arbor/morph/region.hpp index d3d7f735d553202da1070300be948a57619bed47..1cb884b025bde88fdb3440e4f0bd14a1e9dcb8a9 100644 --- a/arbor/include/arbor/morph/region.hpp +++ b/arbor/include/arbor/morph/region.hpp @@ -19,8 +19,6 @@ class em_morphology; class region { public: - region() = delete; - template <typename Impl, typename X=std::enable_if_t<!std::is_same<std::decay_t<Impl>, region>::value>> explicit region(Impl&& impl): @@ -32,6 +30,9 @@ public: region(region&& other) = default; + // The default constructor creates an empty "nil" region. + region(); + region(const region& other): impl_(other.impl_->clone()) {} @@ -110,6 +111,9 @@ private: namespace reg { +// An empty region. +region nil(); + // An explicit cable section. region cable(mcable); diff --git a/arbor/morph/em_morphology.cpp b/arbor/morph/em_morphology.cpp index ba6d275131217ae32a7b175c25e893ee0fb23e31..f00865e3c59259730e020fff7370d715451b0b5e 100644 --- a/arbor/morph/em_morphology.cpp +++ b/arbor/morph/em_morphology.cpp @@ -25,6 +25,8 @@ em_morphology::em_morphology(const morphology& m): const auto ns = morph_.num_samples(); const auto nb = morph_.num_branches(); + if (!ns) return; + // Cache distance of each sample from the root. dist2root_.resize(ns); dist2root_[0] = 0.; @@ -52,7 +54,7 @@ em_morphology::em_morphology(const morphology& m): for (auto i: idx) { sample_locs_[i] = {msize_t(b), (dist2root_[i]-start)/len}; } - // For ensure that all non-spherical branches have their last sample + // Ensure that all non-spherical branches have their last sample if (idx.size()>1u) { sample_locs_[idx.back()] = mlocation{msize_t(b), 1}; } @@ -72,6 +74,10 @@ em_morphology::em_morphology(const morphology& m): } } +em_morphology::em_morphology(): + em_morphology(morphology()) +{} + const morphology& em_morphology::morph() const { return morph_; } diff --git a/arbor/morph/em_morphology.hpp b/arbor/morph/em_morphology.hpp index 94aa5b0fe783551928d3506d5cdc5910d2ea0d5b..1e47dd59b5dd4c32ec33fd1e5bfc7f11ba315063 100644 --- a/arbor/morph/em_morphology.hpp +++ b/arbor/morph/em_morphology.hpp @@ -19,6 +19,7 @@ class em_morphology { std::vector<double> branch_lengths_; public: + em_morphology(); em_morphology(const morphology& m); const morphology& morph() const; diff --git a/arbor/morph/label_dict.cpp b/arbor/morph/label_dict.cpp index a6bf10c8a546f83bd0fed9866eb00a0fff0d85e2..e72c40067ebf50e5618fe65d25ca03e1b7f61a03 100644 --- a/arbor/morph/label_dict.cpp +++ b/arbor/morph/label_dict.cpp @@ -24,16 +24,7 @@ void label_dict::set(const std::string& name, arb::locset ls) { throw morphology_error(util::pprintf( "Attempt to add a locset \"{}\" to a label dictionary that already contains a region with the same name.", name)); } - // First remove an entry with the same name if it exists. - // Has to be this way, because insert_or_assign() is C++17, and we - // can't use operator[] because locset is not default constructable. - auto it = locsets_.find(name); - if (it!=locsets_.end()) { - it->second = std::move(ls); - } - else { - locsets_.emplace(name, std::move(ls)); - } + locsets_[name] = std::move(ls); } void label_dict::set(const std::string& name, arb::region reg) { @@ -41,16 +32,7 @@ void label_dict::set(const std::string& name, arb::region reg) { throw morphology_error(util::pprintf( "Attempt to add a region \"{}\" to a label dictionary that already contains a locset with the same name.", name)); } - // First remove an entry with the same name if it exists. - // Has to be this way, because insert_or_assign() is C++17, and we - // can't use operator[] because region is not default constructable. - auto it = regions_.find(name); - if (it!=regions_.end()) { - it->second = std::move(reg); - } - else { - regions_.emplace(name, std::move(reg)); - } + regions_[name] = std::move(reg); } util::optional<const region&> label_dict::region(const std::string& name) const { diff --git a/arbor/morph/locset.cpp b/arbor/morph/locset.cpp index f1215c4eaf2fa301988eff86736e373b7d8cda09..55c0752ccf366594db1c6fb211e91a4d85fe72f9 100644 --- a/arbor/morph/locset.cpp +++ b/arbor/morph/locset.cpp @@ -244,4 +244,12 @@ locset sum(locset lhs, locset rhs) { return locset(ls::lsum(std::move(lhs), std::move(rhs))); } +locset::locset() { + *this = ls::nil(); +} + +locset::locset(mlocation other) { + *this = ls::location(other); +} + } // namespace arb diff --git a/arbor/morph/morphology.cpp b/arbor/morph/morphology.cpp index 35a73def3deffcf56ec3c0b45146cabde9f6c6d4..ee78c8532ef3a2121aeed365ee0fe1b9cfb53999 100644 --- a/arbor/morph/morphology.cpp +++ b/arbor/morph/morphology.cpp @@ -182,6 +182,10 @@ morphology::morphology(sample_tree m): impl_(std::make_shared<const morphology_impl>(std::move(m))) {} +morphology::morphology(): + morphology(sample_tree()) +{} + // The parent branch of branch b. msize_t morphology::branch_parent(msize_t b) const { return impl_->branch_parents_[b]; diff --git a/arbor/morph/region.cpp b/arbor/morph/region.cpp index 90d999b7fe0a5fcbf4a78aff428987e23c187d61..b456c42e19da81d06fa8d615be573d7e65dede88 100644 --- a/arbor/morph/region.cpp +++ b/arbor/morph/region.cpp @@ -98,6 +98,23 @@ mcable_list remove_cover(mcable_list cables, const em_morphology& m) { return merge(cables); } +// +// Null/empty region +// +struct nil_ {}; + +region nil() { + return region{nil_{}}; +} + +mcable_list thingify_(const nil_& x, const em_morphology& m) { + return {}; +} + +std::ostream& operator<<(std::ostream& o, const nil_& x) { + return o << "nil"; +} + // // Explicit cable section // @@ -300,4 +317,8 @@ region join(region l, region r) { return region{reg::reg_or(std::move(l), std::move(r))}; } +region::region() { + *this = reg::nil(); +} + } // namespace arb diff --git a/example/dryrun/dryrun.cpp b/example/dryrun/dryrun.cpp index e54bc1a70faeb29bc30a3e001e2dbeb1f18678d3..ff300d93951699d26a4557598d91ffccafde49cb 100644 --- a/example/dryrun/dryrun.cpp +++ b/example/dryrun/dryrun.cpp @@ -364,7 +364,7 @@ arb::cable_cell branch_cell(arb::cell_gid_type gid, const cell_parameters& param for (unsigned j=0; j<2; ++j) { if (dis(gen)<bp) { sec_ids.push_back(nsec++); - auto dend = cell.add_cable(sec, arb::section_kind::dendrite, dend_radius, dend_radius, l); + auto dend = cell.add_cable(sec, arb::make_segment<arb::cable_segment>(arb::section_kind::dendrite, dend_radius, dend_radius, l)); dend->set_compartments(nc); dend->add_mechanism("pas"); } @@ -377,10 +377,10 @@ arb::cable_cell branch_cell(arb::cell_gid_type gid, const cell_parameters& param } // Add spike threshold detector at the soma. - cell.add_detector({0,0}, 10); + cell.place(arb::mlocation{0,0}, arb::threshold_detector{10}); // Add a synapse to the mid point of the first dendrite. - cell.add_synapse({1, 0.5}, "expsyn"); + cell.place(arb::mlocation{1, 0.5}, "expsyn"); return cell; } diff --git a/example/gap_junctions/gap_junctions.cpp b/example/gap_junctions/gap_junctions.cpp index 8212beec6b8d181de0cf96d089b6dd59c96d007a..4e3aa846f75176670c1060b23abab22c5812b617 100644 --- a/example/gap_junctions/gap_junctions.cpp +++ b/example/gap_junctions/gap_junctions.cpp @@ -363,23 +363,23 @@ arb::cable_cell gj_cell(cell_gid_type gid, unsigned ncell, double stim_duration) set_reg_params(); setup_seg(soma); - auto dend = cell.add_cable(0, arb::section_kind::dendrite, 3.0/2.0, 3.0/2.0, 300); //cable 1 + auto dend = cell.add_cable(0, arb::make_segment<arb::cable_segment>(arb::section_kind::dendrite, 3.0/2.0, 3.0/2.0, 300)); //cable 1 dend->set_compartments(1); set_reg_params(); setup_seg(dend); - cell.add_detector({0,0}, 10); + cell.place(arb::mlocation{0,0}, arb::threshold_detector{10}); - cell.add_gap_junction({0, 1}); - cell.add_gap_junction({1, 1}); + cell.place(arb::mlocation{0, 1}, arb::gap_junction_site{}); + cell.place(arb::mlocation{1, 1}, arb::gap_junction_site{}); if (!gid) { arb::i_clamp stim(0, stim_duration, 0.4); - cell.add_stimulus({0, 0.5}, stim); + cell.place(arb::mlocation{0, 0.5}, stim); } // Add a synapse to the mid point of the first dendrite. - cell.add_synapse({1, 0.5}, "expsyn"); + cell.place(arb::mlocation{1, 0.5}, "expsyn"); return cell; } diff --git a/example/generators/generators.cpp b/example/generators/generators.cpp index 9c18c5deba2430698db2ee81c934ce1de2335b62..9d48f0c6bb1f999a2785eb564219c3720b44b13a 100644 --- a/example/generators/generators.cpp +++ b/example/generators/generators.cpp @@ -55,7 +55,7 @@ public: // Add one synapse at the soma. // This synapse will be the target for all events, from both // event_generators. - c.add_synapse({0, 0.5}, "expsyn"); + c.place(arb::mlocation{0, 0.5}, "expsyn"); return std::move(c); } diff --git a/example/ring/ring.cpp b/example/ring/ring.cpp index 4cc95891bbe7a41e0c24933a031c1368b7173376..ac7d344f304f569a6a7dbba52a08c200db0b748d 100644 --- a/example/ring/ring.cpp +++ b/example/ring/ring.cpp @@ -367,7 +367,7 @@ arb::cable_cell branch_cell(arb::cell_gid_type gid, const cell_parameters& param for (unsigned j=0; j<2; ++j) { if (dis(gen)<bp) { sec_ids.push_back(nsec++); - auto dend = cell.add_cable(sec, arb::section_kind::dendrite, dend_radius, dend_radius, l); + auto dend = cell.add_cable(sec, arb::make_segment<arb::cable_segment>(arb::section_kind::dendrite, dend_radius, dend_radius, l)); dend->set_compartments(nc); dend->add_mechanism("pas"); } @@ -380,14 +380,14 @@ arb::cable_cell branch_cell(arb::cell_gid_type gid, const cell_parameters& param } // Add spike threshold detector at the soma. - cell.add_detector({0,0}, 10); + cell.place(arb::mlocation{0,0}, arb::threshold_detector{10}); // Add a synapse to the mid point of the first dendrite. - cell.add_synapse({1, 0.5}, "expsyn"); + cell.place(arb::mlocation{1, 0.5}, "expsyn"); // Add additional synapses that will not be connected to anything. for (unsigned i=1u; i<params.synapses; ++i) { - cell.add_synapse({1, 0.5}, "expsyn"); + cell.place(arb::mlocation{1, 0.5}, "expsyn"); } return cell; diff --git a/example/single/single.cpp b/example/single/single.cpp index 6673ef09460ca8352d774391c5b0d42b0531e58c..501969f5de77ca9aafbe9fc7853cfd0945e248a5 100644 --- a/example/single/single.cpp +++ b/example/single/single.cpp @@ -77,7 +77,7 @@ struct single_recipe: public arb::recipe { arb::cell_lid_type last_segment = c.num_segments()-1; arb::mlocation end_last_segment = { last_segment, 1. }; - c.add_synapse(end_last_segment, "exp2syn"); + c.place(end_last_segment, "exp2syn"); return c; } diff --git a/modcc/solvers.cpp b/modcc/solvers.cpp index 1c8ca27e7bf3d0a1cea7bd1766ae00fe92aa1155..c3b42d574c72382f8d2e276960f59d7094b025e1 100644 --- a/modcc/solvers.cpp +++ b/modcc/solvers.cpp @@ -390,7 +390,8 @@ void SparseSolverVisitor::finalize() { // State variable updates given by rhs/diagonal for reduced matrix. Location loc; - for (unsigned i = 0; i<A_.nrow(); ++i) { + auto nrow = A_.nrow(); + for (unsigned i = 0; i<nrow; ++i) { const symge::sym_row& row = A_[i]; unsigned rhs_col = A_.augcol(); unsigned lhs_col = -1; @@ -401,7 +402,7 @@ void SparseSolverVisitor::finalize() { } } - if (lhs_col==-1) { + if (lhs_col==unsigned(-1)) { throw std::logic_error("zero row in sparse solver matrix"); } @@ -480,7 +481,8 @@ void LinearSolverVisitor::finalize() { // State variable updates given by rhs/diagonal for reduced matrix. Location loc; - for (unsigned i = 0; i < A_.nrow(); ++i) { + auto nrow = A_.nrow(); + for (unsigned i = 0; i < nrow; ++i) { const symge::sym_row& row = A_[i]; unsigned rhs = A_.augcol(); unsigned lhs = -1; @@ -491,7 +493,7 @@ void LinearSolverVisitor::finalize() { } } - if (lhs==-1) { + if (lhs==unsigned(-1)) { throw std::logic_error("zero row in linear solver matrix"); } diff --git a/python/cells.cpp b/python/cells.cpp index ed332548bada6508f605b220fb15fc3bdeb5e1d0..21a93db2d8df0b9f6f7c7fc0c38d002e1df48d0f 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -115,7 +115,7 @@ arb::cable_cell make_cable_cell(arb::cell_gid_type gid, const cell_parameters& p for (unsigned j=0; j<2; ++j) { if (dis(gen)<bp) { sec_ids.push_back(nsec++); - auto dend = cell.add_cable(sec, arb::section_kind::dendrite, dend_radius, dend_radius, l); + auto dend = cell.add_cable(sec, arb::make_segment<arb::cable_segment>(arb::section_kind::dendrite, dend_radius, dend_radius, l)); dend->set_compartments(nc); dend->add_mechanism("pas"); } @@ -128,14 +128,14 @@ arb::cable_cell make_cable_cell(arb::cell_gid_type gid, const cell_parameters& p } // Add spike threshold detector at the soma. - cell.add_detector({0,0}, 10); + cell.place(arb::mlocation{0,0}, arb::threshold_detector{10}); // Add a synapse to the mid point of the first dendrite. - cell.add_synapse({1, 0.5}, "expsyn"); + cell.place(arb::mlocation{1, 0.5}, "expsyn"); // Add additional synapses. for (unsigned i=1u; i<params.synapses; ++i) { - cell.add_synapse({1, 0.5}, "expsyn"); + cell.place(arb::mlocation{1, 0.5}, "expsyn"); } return cell; diff --git a/test/common_cells.hpp b/test/common_cells.hpp index 0694bf6daf874a97904bde52815fb77b5dd52eab..dd40bc91d0add02c8f101651d685d1bc6ab8a316 100644 --- a/test/common_cells.hpp +++ b/test/common_cells.hpp @@ -27,7 +27,7 @@ inline cable_cell make_cell_soma_only(bool with_stim = true) { soma->add_mechanism("hh"); if (with_stim) { - c.add_stimulus({0,0.5}, {10., 100., 0.1}); + c.place(mlocation{0,0.5}, i_clamp{10., 100., 0.1}); } return c; @@ -60,7 +60,7 @@ inline cable_cell make_cell_ball_and_stick(bool with_stim = true) { auto soma = c.add_soma(12.6157/2.0); soma->add_mechanism("hh"); - c.add_cable(0, section_kind::dendrite, 1.0/2, 1.0/2, 200.0); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 1.0/2, 1.0/2, 200.0)); for (auto& seg: c.segments()) { if (seg->is_dendrite()) { @@ -70,7 +70,7 @@ inline cable_cell make_cell_ball_and_stick(bool with_stim = true) { } if (with_stim) { - c.add_stimulus({1,1}, {5., 80., 0.3}); + c.place(mlocation{1,1}, i_clamp{5., 80., 0.3}); } return c; } @@ -104,7 +104,7 @@ inline cable_cell make_cell_ball_and_taper(bool with_stim = true) { auto soma = c.add_soma(12.6157/2.0); soma->add_mechanism("hh"); - c.add_cable(0, section_kind::dendrite, 1.0/2, 0.4/2, 200.0); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 1.0/2, 0.4/2, 200.0)); for (auto& seg: c.segments()) { if (seg->is_dendrite()) { @@ -114,7 +114,7 @@ inline cable_cell make_cell_ball_and_taper(bool with_stim = true) { } if (with_stim) { - c.add_stimulus({1,1}, {5., 80., 0.3}); + c.place(mlocation{1,1}, i_clamp{5., 80., 0.3}); } return c; } @@ -172,7 +172,7 @@ inline cable_cell make_cell_ball_and_squiggle(bool with_stim = true) { } if (with_stim) { - c.add_stimulus({1,1}, {5., 80., 0.3}); + c.place(mlocation{1,1}, i_clamp{5., 80., 0.3}); } return c; } @@ -207,9 +207,9 @@ inline cable_cell make_cell_ball_and_3stick(bool with_stim = true) { auto soma = c.add_soma(12.6157/2.0); soma->add_mechanism("hh"); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 100); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 100); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); for (auto& seg: c.segments()) { if (seg->is_dendrite()) { @@ -219,8 +219,8 @@ inline cable_cell make_cell_ball_and_3stick(bool with_stim = true) { } if (with_stim) { - c.add_stimulus({2,1}, {5., 80., 0.45}); - c.add_stimulus({3,1}, {40., 10.,-0.2}); + c.place(mlocation{2,1}, i_clamp{5., 80., 0.45}); + c.place(mlocation{3,1}, i_clamp{40., 10.,-0.2}); } return c; } @@ -253,7 +253,7 @@ inline cable_cell make_cell_simple_cable(bool with_stim = true) { c.default_parameters.membrane_capacitance = 0.01; c.add_soma(0); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 1000); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 1000)); double gbar = 0.000025; double I = 0.1; @@ -272,7 +272,7 @@ inline cable_cell make_cell_simple_cable(bool with_stim = true) { if (with_stim) { // stimulus in the middle of our zero-volume 'soma' // corresponds to proximal end of cable. - c.add_stimulus({0,0.5}, {0., INFINITY, I}); + c.place(mlocation{0,0.5}, i_clamp{0., INFINITY, I}); } return c; } diff --git a/test/unit/test_cable_cell.cpp b/test/unit/test_cable_cell.cpp index f297a50d5c4035ce8d9981604e5297827cf1ffb0..49d16a9e74b4e82d79f4a7f4b7350d0e8423db96 100644 --- a/test/unit/test_cable_cell.cpp +++ b/test/unit/test_cable_cell.cpp @@ -2,6 +2,7 @@ #include <arbor/cable_cell.hpp> #include <arbor/math.hpp> +#include <arbor/morph/locset.hpp> #include "tree.hpp" @@ -37,9 +38,6 @@ TEST(cable_cell, soma) { auto s = c.soma(); EXPECT_EQ(s->radius(), soma_radius); EXPECT_EQ(s->center().is_set(), true); - - // add expression template library for points - //EXPECT_EQ(s->center(), point<double>(0,0,1)); } } @@ -78,7 +76,7 @@ TEST(cable_cell, add_segment) { c.add_cable( 0, - section_kind::dendrite, cable_radius, cable_radius, cable_length + make_segment<cable_segment>(section_kind::dendrite, cable_radius, cable_radius, cable_length) ); EXPECT_EQ(c.num_segments(), 2u); @@ -95,9 +93,9 @@ TEST(cable_cell, add_segment) { c.add_cable( 0, - section_kind::dendrite, - std::vector<double>{cable_radius, cable_radius, cable_radius, cable_radius}, - std::vector<double>{cable_length, cable_length, cable_length} + make_segment<cable_segment>(section_kind::dendrite, + std::vector<double>{cable_radius, cable_radius, cable_radius, cable_radius}, + std::vector<double>{cable_length, cable_length, cable_length}) ); EXPECT_EQ(c.num_segments(), 2u); @@ -196,14 +194,13 @@ TEST(cable_cell, clone) { cable_cell c; c.add_soma(2.1); - c.add_cable(0, section_kind::dendrite, 0.3, 0.2, 10); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.2, 10)); c.segment(1)->set_compartments(3); - c.add_cable(1, section_kind::dendrite, 0.2, 0.15, 20); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.2, 0.15, 20)); c.segment(2)->set_compartments(5); - c.add_synapse({1, 0.3}, "expsyn"); - - c.add_detector({0, 0.5}, 10.0); + c.place(mlocation{1, 0.3}, "expsyn"); + c.place(mlocation{0, 0.5}, threshold_detector{10.0}); // make clone @@ -230,20 +227,10 @@ TEST(cable_cell, clone) { // check clone is independent - c.add_cable(2, section_kind::dendrite, 0.15, 0.1, 20); + c.add_cable(2, make_segment<cable_segment>(section_kind::dendrite, 0.15, 0.1, 20)); EXPECT_NE(c.num_segments(), d.num_segments()); - d.detectors()[0].threshold = 13.0; - ASSERT_EQ(1u, c.detectors().size()); - ASSERT_EQ(1u, d.detectors().size()); - EXPECT_NE(c.detectors()[0].threshold, d.detectors()[0].threshold); - c.segment(1)->set_compartments(7); EXPECT_NE(c.segment(1)->num_compartments(), d.segment(1)->num_compartments()); EXPECT_EQ(c.segment(2)->num_compartments(), d.segment(2)->num_compartments()); } - -TEST(cable_cell, get_kind) { - cable_cell c; - EXPECT_EQ(cell_kind::cable, c.get_cell_kind()); -} diff --git a/test/unit/test_domain_decomposition.cpp b/test/unit/test_domain_decomposition.cpp index 9acd94e09dbe8888f6b17a40295b2d291f33f9fd..30524dec9ff0aac23ea52dc61f86c753227b50ae 100644 --- a/test/unit/test_domain_decomposition.cpp +++ b/test/unit/test_domain_decomposition.cpp @@ -66,7 +66,7 @@ namespace { arb::util::unique_any get_cell_description(cell_gid_type) const override { cable_cell c; c.add_soma(20); - c.add_gap_junction({0,1}); + c.place(mlocation{0,1}, gap_junction_site{}); return {std::move(c)}; } diff --git a/test/unit/test_event_delivery.cpp b/test/unit/test_event_delivery.cpp index cafba40f1982fde675cccc26d0cce059b6a7e320..a4d4b632c7edca338f6719c31a6c2181aa3c9713 100644 --- a/test/unit/test_event_delivery.cpp +++ b/test/unit/test_event_delivery.cpp @@ -29,9 +29,9 @@ struct test_recipe: public n_cable_cell_recipe { static cable_cell test_cell() { cable_cell c; c.add_soma(10.)->add_mechanism("pas"); - c.add_synapse({0, 0.5}, "expsyn"); - c.add_detector({0, 0.5}, -64); - c.add_gap_junction({0, 0.5}); + c.place(mlocation{0, 0.5}, "expsyn"); + c.place(mlocation{0, 0.5}, threshold_detector{-64}); + c.place(mlocation{0, 0.5}, gap_junction_site{}); return c; } diff --git a/test/unit/test_fvm_layout.cpp b/test/unit/test_fvm_layout.cpp index 3bd981cd580c884e1dd9fcbaef2af08f4785fce1..6ad308015644738ab31c73b50fdd30897cc85dc5 100644 --- a/test/unit/test_fvm_layout.cpp +++ b/test/unit/test_fvm_layout.cpp @@ -10,6 +10,7 @@ #include "util/maputil.hpp" #include "util/rangeutil.hpp" #include "util/span.hpp" +#include "io/sepval.hpp" #include "common.hpp" #include "unit_test_catalogue.hpp" @@ -99,17 +100,17 @@ namespace { s = c2.add_soma(14./2); s->add_mechanism("hh"); - s = c2.add_cable(0, section_kind::dendrite, 1.0/2, 1.0/2, 200); + s = c2.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 1.0/2, 1.0/2, 200)); s->parameters.membrane_capacitance = 0.017; - s = c2.add_cable(1, section_kind::dendrite, 0.8/2, 0.8/2, 300); + s = c2.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.8/2, 0.8/2, 300)); s->parameters.membrane_capacitance = 0.013; - s = c2.add_cable(1, section_kind::dendrite, 0.7/2, 0.7/2, 180); + s = c2.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.7/2, 0.7/2, 180)); s->parameters.membrane_capacitance = 0.018; - c2.add_stimulus({2,1}, {5., 80., 0.45}); - c2.add_stimulus({3,1}, {40., 10.,-0.2}); + c2.place(mlocation{2,1}, i_clamp{5., 80., 0.45}); + c2.place(mlocation{3,1}, i_clamp{40., 10.,-0.2}); for (auto& seg: c2.segments()) { if (seg->is_dendrite()) { @@ -289,10 +290,10 @@ TEST(fvm_layout, mech_index) { check_two_cell_system(cells); // Add four synapses of two varieties across the cells. - cells[0].add_synapse({1, 0.4}, "expsyn"); - cells[0].add_synapse({1, 0.4}, "expsyn"); - cells[1].add_synapse({2, 0.4}, "exp2syn"); - cells[1].add_synapse({3, 0.4}, "expsyn"); + cells[0].place(mlocation{1, 0.4}, "expsyn"); + cells[0].place(mlocation{1, 0.4}, "expsyn"); + cells[1].place(mlocation{2, 0.4}, "exp2syn"); + cells[1].place(mlocation{3, 0.4}, "expsyn"); cable_cell_global_properties gprop; gprop.default_parameters = neuron_parameter_defaults; @@ -337,9 +338,49 @@ TEST(fvm_layout, mech_index) { EXPECT_EQ(ivec({0,6}), M.ions.at("k"s).cv); } +struct exp_instance { + int cv; + int multiplicity; + std::vector<unsigned> targets; + double e; + double tau; + + template <typename Seq> + exp_instance(int cv, const Seq& tgts, double e, double tau): + cv(cv), multiplicity(util::size(tgts)), e(e), tau(tau) + { + targets.reserve(util::size(tgts)); + for (auto t: tgts) targets.push_back(t); + util::sort(targets); + } + + bool matches(const exp_instance& I) const { + return I.cv==cv && I.e==e && I.tau==tau && I.targets==targets; + } + + bool is_in(const arb::fvm_mechanism_config& C) const { + std::vector<unsigned> _; + auto part = util::make_partition(_, C.multiplicity); + auto& evals = *value_by_key(C.param_values, "e"); + // Handle both expsyn and exp2syn by looking for "tau1" if "tau" + // parameter is not found. + auto& tauvals = value_by_key(C.param_values, "tau")? + *value_by_key(C.param_values, "tau"): + *value_by_key(C.param_values, "tau1"); + + for (auto i: make_span(C.multiplicity.size())) { + exp_instance other(C.cv[i], + util::subrange_view(C.target, part[i]), + evals[i], + tauvals[i]); + if (matches(other)) return true; + } + return false; + } +}; + TEST(fvm_layout, coalescing_synapses) { using ivec = std::vector<fvm_index_type>; - using fvec = std::vector<fvm_value_type>; auto syn_desc = [&](const char* name, double val0, double val1) { mechanism_desc m(name); @@ -363,14 +404,16 @@ TEST(fvm_layout, coalescing_synapses) { gprop_coalesce.default_parameters = neuron_parameter_defaults; gprop_coalesce.coalesce_synapses = true; + using L=std::initializer_list<unsigned>; + { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.3}, "expsyn"); - cell.add_synapse({1, 0.5}, "expsyn"); - cell.add_synapse({1, 0.7}, "expsyn"); - cell.add_synapse({1, 0.9}, "expsyn"); + cell.place(mlocation{1, 0.3}, "expsyn"); + cell.place(mlocation{1, 0.5}, "expsyn"); + cell.place(mlocation{1, 0.7}, "expsyn"); + cell.place(mlocation{1, 0.9}, "expsyn"); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_coalesce, {cell}, D); @@ -383,10 +426,10 @@ TEST(fvm_layout, coalescing_synapses) { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.3}, "expsyn"); - cell.add_synapse({1, 0.5}, "exp2syn"); - cell.add_synapse({1, 0.7}, "expsyn"); - cell.add_synapse({1, 0.9}, "exp2syn"); + cell.place(mlocation{1, 0.3}, "expsyn"); + cell.place(mlocation{1, 0.5}, "exp2syn"); + cell.place(mlocation{1, 0.7}, "expsyn"); + cell.place(mlocation{1, 0.9}, "exp2syn"); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_coalesce, {cell}, D); @@ -402,10 +445,10 @@ TEST(fvm_layout, coalescing_synapses) { { cable_cell cell = make_cell_ball_and_stick(); - cell.add_synapse({1, 0.3}, "expsyn"); - cell.add_synapse({1, 0.5}, "expsyn"); - cell.add_synapse({1, 0.7}, "expsyn"); - cell.add_synapse({1, 0.9}, "expsyn"); + cell.place(mlocation{1, 0.3}, "expsyn"); + cell.place(mlocation{1, 0.5}, "expsyn"); + cell.place(mlocation{1, 0.7}, "expsyn"); + cell.place(mlocation{1, 0.9}, "expsyn"); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_no_coalesce, {cell}, D); @@ -418,10 +461,10 @@ TEST(fvm_layout, coalescing_synapses) { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.3}, "expsyn"); - cell.add_synapse({1, 0.5}, "exp2syn"); - cell.add_synapse({1, 0.7}, "expsyn"); - cell.add_synapse({1, 0.9}, "exp2syn"); + cell.place(mlocation{1, 0.3}, "expsyn"); + cell.place(mlocation{1, 0.5}, "exp2syn"); + cell.place(mlocation{1, 0.7}, "expsyn"); + cell.place(mlocation{1, 0.9}, "exp2syn"); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_no_coalesce, {cell}, D); @@ -438,10 +481,10 @@ TEST(fvm_layout, coalescing_synapses) { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.3}, "expsyn"); - cell.add_synapse({1, 0.3}, "expsyn"); - cell.add_synapse({1, 0.7}, "expsyn"); - cell.add_synapse({1, 0.7}, "expsyn"); + cell.place(mlocation{1, 0.3}, "expsyn"); + cell.place(mlocation{1, 0.3}, "expsyn"); + cell.place(mlocation{1, 0.7}, "expsyn"); + cell.place(mlocation{1, 0.7}, "expsyn"); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_coalesce, {cell}, D); @@ -454,74 +497,80 @@ TEST(fvm_layout, coalescing_synapses) { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 0, 0.2)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 0, 0.2)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 0.1, 0.2)); - cell.add_synapse({1, 0.7}, syn_desc("expsyn", 0.1, 0.2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 0, 0.2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 0, 0.2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 0.1, 0.2)); + cell.place(mlocation{1, 0.7}, syn_desc("expsyn", 0.1, 0.2)); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_coalesce, {cell}, D); - auto &expsyn_config = M.mechanisms.at("expsyn"); - EXPECT_EQ(ivec({2, 2, 4}), expsyn_config.cv); - EXPECT_EQ(ivec({2, 1, 1}), expsyn_config.multiplicity); - EXPECT_EQ(fvec({0, 0.1, 0.1}), expsyn_config.param_values[0].second); - EXPECT_EQ(fvec({0.2, 0.2, 0.2}), expsyn_config.param_values[1].second); + std::vector<exp_instance> instances{ + exp_instance(2, L{0, 1}, 0., 0.2), + exp_instance(2, L{2}, 0.1, 0.2), + exp_instance(4, L{3}, 0.1, 0.2), + }; + auto& config = M.mechanisms.at("expsyn"); + for (auto& instance: instances) { + EXPECT_TRUE(instance.is_in(config)); + } } { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.7}, syn_desc("expsyn", 0, 3)); - cell.add_synapse({1, 0.7}, syn_desc("expsyn", 1, 3)); - cell.add_synapse({1, 0.7}, syn_desc("expsyn", 0, 3)); - cell.add_synapse({1, 0.7}, syn_desc("expsyn", 1, 3)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 0, 2)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 1, 2)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 0, 2)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 1, 2)); + cell.place(mlocation{1, 0.7}, syn_desc("expsyn", 0, 3)); + cell.place(mlocation{1, 0.7}, syn_desc("expsyn", 1, 3)); + cell.place(mlocation{1, 0.7}, syn_desc("expsyn", 0, 3)); + cell.place(mlocation{1, 0.7}, syn_desc("expsyn", 1, 3)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 0, 2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 1, 2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 0, 2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 1, 2)); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_coalesce, {cell}, D); - auto &expsyn_config = M.mechanisms.at("expsyn"); - EXPECT_EQ(ivec({2, 2, 4, 4}), expsyn_config.cv); - EXPECT_EQ(ivec({4, 6, 5, 7, 0, 2, 1, 3}), expsyn_config.target); - EXPECT_EQ(ivec({2, 2, 2, 2}), expsyn_config.multiplicity); - EXPECT_EQ(fvec({0, 1, 0, 1}), expsyn_config.param_values[0].second); - EXPECT_EQ(fvec({2, 2, 3, 3}), expsyn_config.param_values[1].second); + std::vector<exp_instance> instances{ + exp_instance(2, L{4, 6}, 0.0, 2.0), + exp_instance(2, L{5, 7}, 1.0, 2.0), + exp_instance(4, L{0, 2}, 0.0, 3.0), + exp_instance(4, L{1, 3}, 1.0, 3.0), + }; + auto& config = M.mechanisms.at("expsyn"); + for (auto& instance: instances) { + EXPECT_TRUE(instance.is_in(config)); + } } { cable_cell cell = make_cell_ball_and_stick(); // Add synapses of two varieties. - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 1, 2)); - cell.add_synapse({1, 0.3}, syn_desc_2("exp2syn", 4, 1)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 1, 2)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 5, 1)); - cell.add_synapse({1, 0.3}, syn_desc_2("exp2syn", 1, 3)); - cell.add_synapse({1, 0.3}, syn_desc("expsyn", 1, 2)); - cell.add_synapse({1, 0.7}, syn_desc_2("exp2syn", 2, 2)); - cell.add_synapse({1, 0.7}, syn_desc_2("exp2syn", 2, 1)); - cell.add_synapse({1, 0.7}, syn_desc_2("exp2syn", 2, 1)); - cell.add_synapse({1, 0.7}, syn_desc_2("exp2syn", 2, 2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 1, 2)); + cell.place(mlocation{1, 0.3}, syn_desc_2("exp2syn", 4, 1)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 1, 2)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 5, 1)); + cell.place(mlocation{1, 0.3}, syn_desc_2("exp2syn", 1, 3)); + cell.place(mlocation{1, 0.3}, syn_desc("expsyn", 1, 2)); + cell.place(mlocation{1, 0.7}, syn_desc_2("exp2syn", 2, 2)); + cell.place(mlocation{1, 0.7}, syn_desc_2("exp2syn", 2, 1)); + cell.place(mlocation{1, 0.7}, syn_desc_2("exp2syn", 2, 1)); + cell.place(mlocation{1, 0.7}, syn_desc_2("exp2syn", 2, 2)); fvm_discretization D = fvm_discretize({cell}, neuron_parameter_defaults); fvm_mechanism_data M = fvm_build_mechanism_data(gprop_coalesce, {cell}, D); - auto &expsyn_config = M.mechanisms.at("expsyn"); - EXPECT_EQ(ivec({2, 2}), expsyn_config.cv); - EXPECT_EQ(ivec({0, 2, 5, 3}), expsyn_config.target); - EXPECT_EQ(ivec({3, 1}), expsyn_config.multiplicity); - EXPECT_EQ(fvec({1, 5}), expsyn_config.param_values[0].second); - EXPECT_EQ(fvec({2, 1}), expsyn_config.param_values[1].second); + for (auto &instance: {exp_instance(2, L{0,2,5}, 1, 2), + exp_instance(2, L{3}, 5, 1)}) { + EXPECT_TRUE(instance.is_in(M.mechanisms.at("expsyn"))); + } - auto &exp2syn_config = M.mechanisms.at("exp2syn"); - EXPECT_EQ(ivec({2, 2, 4, 4}), exp2syn_config.cv); - EXPECT_EQ(ivec({4, 1, 7, 8, 6, 9}), exp2syn_config.target); - EXPECT_EQ(ivec({1, 1, 2, 2}), exp2syn_config.multiplicity); - EXPECT_EQ(fvec({1, 4, 2, 2}), exp2syn_config.param_values[0].second); - EXPECT_EQ(fvec({3, 1, 1, 2}), exp2syn_config.param_values[1].second); + for (auto &instance: {exp_instance(2, L{4}, 1, 3), + exp_instance(2, L{1}, 4, 1), + exp_instance(4, L{7,8}, 2, 1), + exp_instance(4, L{6,9}, 2, 2)}) { + EXPECT_TRUE(instance.is_in(M.mechanisms.at("exp2syn"))); + } } } @@ -543,14 +592,14 @@ TEST(fvm_layout, synapse_targets) { return mechanism_desc(name).set("e", syn_e.at(idx)); }; - cells[0].add_synapse({1, 0.9}, syn_desc("expsyn", 0)); - cells[0].add_synapse({0, 0.5}, syn_desc("expsyn", 1)); - cells[0].add_synapse({1, 0.4}, syn_desc("expsyn", 2)); + cells[0].place(mlocation{1, 0.9}, syn_desc("expsyn", 0)); + cells[0].place(mlocation{0, 0.5}, syn_desc("expsyn", 1)); + cells[0].place(mlocation{1, 0.4}, syn_desc("expsyn", 2)); - cells[1].add_synapse({2, 0.4}, syn_desc("exp2syn", 3)); - cells[1].add_synapse({1, 0.4}, syn_desc("exp2syn", 4)); - cells[1].add_synapse({3, 0.4}, syn_desc("expsyn", 5)); - cells[1].add_synapse({3, 0.7}, syn_desc("exp2syn", 6)); + cells[1].place(mlocation{2, 0.4}, syn_desc("exp2syn", 3)); + cells[1].place(mlocation{1, 0.4}, syn_desc("exp2syn", 4)); + cells[1].place(mlocation{3, 0.4}, syn_desc("expsyn", 5)); + cells[1].place(mlocation{3, 0.7}, syn_desc("exp2syn", 6)); cable_cell_global_properties gprop; gprop.default_parameters = neuron_parameter_defaults; @@ -639,9 +688,9 @@ TEST(fvm_layout, density_norm_area) { cable_cell& c = cells[0]; auto soma = c.add_soma(12.6157/2.0); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100); - c.add_cable(1, section_kind::dendrite, 0.5, 0.1, 200); - c.add_cable(1, section_kind::dendrite, 0.4, 0.4, 150); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.1, 200)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.4, 0.4, 150)); auto& segs = c.segments(); @@ -803,9 +852,9 @@ TEST(fvm_layout, ion_weights) { auto construct_cell = [](cable_cell& c) { c.add_soma(5); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 200); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 100); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 200)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); for (auto& s: c.segments()) s->set_compartments(1); }; @@ -875,9 +924,9 @@ TEST(fvm_layout, revpot) { auto construct_cell = [](cable_cell& c) { c.add_soma(5); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 200); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 100); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 200)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); for (auto& s: c.segments()) s->set_compartments(1); diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp index 42726b6b15511743a58fdec44518521cc4f23fc9..a82132f497579e916fee647bd3a7fab7c84b6798 100644 --- a/test/unit/test_fvm_lowered.cpp +++ b/test/unit/test_fvm_lowered.cpp @@ -94,7 +94,7 @@ public: arb::util::unique_any get_cell_description(cell_gid_type gid) const override { cable_cell c; c.add_soma(20); - c.add_gap_junction({0, 1}); + c.place(mlocation{0, 1}, gap_junction_site{}); return {std::move(c)}; } @@ -162,7 +162,7 @@ public: arb::util::unique_any get_cell_description(cell_gid_type) const override { cable_cell c; c.add_soma(20); - c.add_gap_junction({0,1}); + c.place(mlocation{0,1}, gap_junction_site{}); return {std::move(c)}; } @@ -257,12 +257,12 @@ TEST(fvm_lowered, target_handles) { EXPECT_EQ(cells[1].num_segments(), 4u); // (in increasing target order) - cells[0].add_synapse({1, 0.4}, "expsyn"); - cells[0].add_synapse({0, 0.5}, "expsyn"); - cells[1].add_synapse({2, 0.2}, "exp2syn"); - cells[1].add_synapse({2, 0.8}, "expsyn"); + cells[0].place(mlocation{1, 0.4}, "expsyn"); + cells[0].place(mlocation{0, 0.5}, "expsyn"); + cells[1].place(mlocation{2, 0.2}, "exp2syn"); + cells[1].place(mlocation{2, 0.8}, "expsyn"); - cells[1].add_detector({0, 0}, 3.3); + cells[1].place(mlocation{0, 0}, threshold_detector{3.3}); std::vector<target_handle> targets; std::vector<fvm_index_type> cell_to_intdom; @@ -321,8 +321,8 @@ TEST(fvm_lowered, stimulus) { std::vector<cable_cell> cells; cells.push_back(make_cell_ball_and_stick(false)); - cells[0].add_stimulus({1,1}, {5., 80., 0.3}); - cells[0].add_stimulus({0,0.5}, {1., 2., 0.1}); + cells[0].place(mlocation{1,1}, i_clamp{5., 80., 0.3}); + cells[0].place(mlocation{0,0.5}, i_clamp{1., 2., 0.1}); const fvm_size_type soma_cv = 0u; const fvm_size_type tip_cv = 5u; @@ -401,7 +401,7 @@ TEST(fvm_lowered, derived_mechs) { for (int i = 0; i<3; ++i) { cable_cell& c = cells[i]; c.add_soma(6.0); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); c.segment(1)->set_compartments(4); for (auto& seg: c.segments()) { @@ -612,7 +612,7 @@ TEST(fvm_lowered, point_ionic_current) { double soma_area_m2 = 4*math::pi<double>*r*r*1e-12; // [m²] // Event weight is translated by point_ica_current into a current contribution in nA. - c.add_synapse({0u, 0.5}, "point_ica_current"); + c.place(mlocation{0u, 0.5}, "point_ica_current"); cable1d_recipe rec(c); rec.catalogue() = make_unit_test_catalogue(); @@ -676,9 +676,9 @@ TEST(fvm_lowered, weighted_write_ion) { cable_cell c; c.add_soma(5); - c.add_cable(0, section_kind::dendrite, 0.5, 0.5, 100); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 200); - c.add_cable(1, section_kind::dendrite, 0.5, 0.5, 100); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 200)); + c.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.5, 0.5, 100)); for (auto& s: c.segments()) s->set_compartments(1); @@ -770,15 +770,15 @@ TEST(fvm_lowered, gj_coords_simple) { std::vector<cable_cell> cells; cable_cell c, d; c.add_soma(2.1); - c.add_cable(0, section_kind::dendrite, 0.3, 0.2, 10); + c.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.2, 10)); c.segment(1)->set_compartments(5); - c.add_gap_junction({1, 0.8}); + c.place(mlocation{1, 0.8}, gap_junction_site{}); cells.push_back(std::move(c)); d.add_soma(2.4); - d.add_cable(0, section_kind::dendrite, 0.3, 0.2, 10); + d.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.2, 10)); d.segment(1)->set_compartments(2); - d.add_gap_junction({1, 1}); + d.place(mlocation{1, 1}, gap_junction_site{}); cells.push_back(std::move(d)); fvm_discretization D = fvm_discretize(cells, neuron_parameter_defaults); @@ -846,41 +846,41 @@ TEST(fvm_lowered, gj_coords_complex) { // Make 3 cells c0.add_soma(2.1); - c0.add_cable(0, section_kind::dendrite, 0.3, 0.2, 8); + c0.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.2, 8)); c0.segment(1)->set_compartments(4); c1.add_soma(1.4); - c1.add_cable(0, section_kind::dendrite, 0.3, 0.5, 12); + c1.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.5, 12)); c1.segment(1)->set_compartments(6); - c1.add_cable(1, section_kind::dendrite, 0.3, 0.2, 9); + c1.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.2, 9)); c1.segment(2)->set_compartments(3); - c1.add_cable(1, section_kind::dendrite, 0.2, 0.2, 15); + c1.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.2, 0.2, 15)); c1.segment(3)->set_compartments(5); c2.add_soma(2.9); - c2.add_cable(0, section_kind::dendrite, 0.3, 0.5, 4); + c2.add_cable(0, make_segment<cable_segment>(section_kind::dendrite, 0.3, 0.5, 4)); c2.segment(1)->set_compartments(2); - c2.add_cable(1, section_kind::dendrite, 0.4, 0.2, 6); + c2.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.4, 0.2, 6)); c2.segment(2)->set_compartments(2); - c2.add_cable(1, section_kind::dendrite, 0.1, 0.2, 8); + c2.add_cable(1, make_segment<cable_segment>(section_kind::dendrite, 0.1, 0.2, 8)); c2.segment(3)->set_compartments(2); - c2.add_cable(2, section_kind::dendrite, 0.2, 0.2, 4); + c2.add_cable(2, make_segment<cable_segment>(section_kind::dendrite, 0.2, 0.2, 4)); c2.segment(4)->set_compartments(2); - c2.add_cable(2, section_kind::dendrite, 0.2, 0.2, 4); + c2.add_cable(2, make_segment<cable_segment>(section_kind::dendrite, 0.2, 0.2, 4)); c2.segment(5)->set_compartments(2); // Add 5 gap junctions - c0.add_gap_junction({1, 1}); - c0.add_gap_junction({1, 0.5}); + c0.place(mlocation{1, 1}, gap_junction_site{}); + c0.place(mlocation{1, 0.5}, gap_junction_site{}); - c1.add_gap_junction({2, 1}); - c1.add_gap_junction({1, 1}); - c1.add_gap_junction({1, 0.45}); - c1.add_gap_junction({1, 0.1}); + c1.place(mlocation{2, 1}, gap_junction_site{}); + c1.place(mlocation{1, 1}, gap_junction_site{}); + c1.place(mlocation{1, 0.45}, gap_junction_site{}); + c1.place(mlocation{1, 0.1}, gap_junction_site{}); - c2.add_gap_junction({1, 0.5}); - c2.add_gap_junction({4, 1}); - c2.add_gap_junction({2, 1}); + c2.place(mlocation{1, 0.5}, gap_junction_site{}); + c2.place(mlocation{4, 1}, gap_junction_site{}); + c2.place(mlocation{2, 1}, gap_junction_site{}); cells.push_back(std::move(c0)); cells.push_back(std::move(c1)); @@ -958,7 +958,7 @@ TEST(fvm_lowered, cell_group_gj) { cable_cell c; c.add_soma(2.1); if (i % 2 == 0) { - c.add_gap_junction({0, 1}); + c.place(mlocation{0, 1}, gap_junction_site{}); } if (i < 10) { cell_group0.push_back(std::move(c)); diff --git a/test/unit/test_mc_cell_group.cpp b/test/unit/test_mc_cell_group.cpp index 5a453dd6d6056979e14c311e97222f05c26ca330..25415273f1d19c78a1fc0ec5710ff3eeb94f0c87 100644 --- a/test/unit/test_mc_cell_group.cpp +++ b/test/unit/test_mc_cell_group.cpp @@ -23,7 +23,7 @@ namespace { cable_cell make_cell() { auto c = make_cell_ball_and_stick(); - c.add_detector({0, 0}, 0); + c.place(mlocation{0, 0}, threshold_detector{0}); c.segment(1)->set_compartments(101); return c; @@ -63,7 +63,7 @@ TEST(mc_cell_group, sources) { for (int i=0; i<20; ++i) { cells.push_back(make_cell()); if (i==0 || i==3 || i==17) { - cells.back().add_detector({1, 0.3}, 2.3); + cells.back().place(mlocation{1, 0.3}, threshold_detector{2.3}); } EXPECT_EQ(1u + (i==0 || i==3 || i==17), cells.back().detectors().size()); diff --git a/test/unit/test_mc_cell_group_gpu.cpp b/test/unit/test_mc_cell_group_gpu.cpp index 58524dce64d37c09177d667897a043376ac9a62b..05b3d68193969ea63461e956fc3be763948b26cc 100644 --- a/test/unit/test_mc_cell_group_gpu.cpp +++ b/test/unit/test_mc_cell_group_gpu.cpp @@ -21,7 +21,7 @@ namespace { cable_cell make_cell() { auto c = make_cell_ball_and_stick(); - c.add_detector({0, 0}, 0); + c.place(mlocation{0, 0}, threshold_detector{0}); c.segment(1)->set_compartments(101); return c; diff --git a/test/unit/test_probe.cpp b/test/unit/test_probe.cpp index 5afba4f3e89a1f040eaf2e6764a4d58b49192464..7ac92d3c395a403d72ae9ef15f76535b148ac4ad 100644 --- a/test/unit/test_probe.cpp +++ b/test/unit/test_probe.cpp @@ -24,7 +24,7 @@ TEST(probe, fvm_lowered_cell) { cable_cell bs = make_cell_ball_and_stick(false); i_clamp stim(0, 100, 0.3); - bs.add_stimulus({1, 1}, stim); + bs.place(mlocation{1, 1}, stim); cable1d_recipe rec(bs); diff --git a/test/unit/test_synapses.cpp b/test/unit/test_synapses.cpp index b7983f1ebadbb90924da1a461c3c1b52b2a68ee6..03695d9345eebe3cbc8cf267d0ee50810d6a7d0d 100644 --- a/test/unit/test_synapses.cpp +++ b/test/unit/test_synapses.cpp @@ -38,9 +38,9 @@ TEST(synapses, add_to_cell) { auto soma = cell.add_soma(12.6157/2.0); soma->add_mechanism("hh"); - cell.add_synapse({0, 0.1}, "expsyn"); - cell.add_synapse({1, 0.2}, "exp2syn"); - cell.add_synapse({0, 0.3}, "expsyn"); + cell.place(mlocation{0, 0.1}, "expsyn"); + cell.place(mlocation{0, 0.2}, "exp2syn"); + cell.place(mlocation{0, 0.3}, "expsyn"); EXPECT_EQ(3u, cell.synapses().size()); const auto& syns = cell.synapses(); @@ -49,13 +49,16 @@ TEST(synapses, add_to_cell) { EXPECT_EQ(syns[0].location.pos, 0.1); EXPECT_EQ(syns[0].mechanism.name(), "expsyn"); - EXPECT_EQ(syns[1].location.branch, 1u); + EXPECT_EQ(syns[1].location.branch, 0u); EXPECT_EQ(syns[1].location.pos, 0.2); EXPECT_EQ(syns[1].mechanism.name(), "exp2syn"); EXPECT_EQ(syns[2].location.branch, 0u); EXPECT_EQ(syns[2].location.pos, 0.3); EXPECT_EQ(syns[2].mechanism.name(), "expsyn"); + + // adding a synapse to an invalid branch location should throw. + EXPECT_THROW(cell.place(mlocation{1, 0.3}, "expsyn"), arb::cable_cell_error); } template <typename Seq>