diff --git a/arbor/CMakeLists.txt b/arbor/CMakeLists.txt index 8208a98e7ce91ae783799b595c2340d393afca93..4ea56fd86690a783836cce92ae46e951b12f99fa 100644 --- a/arbor/CMakeLists.txt +++ b/arbor/CMakeLists.txt @@ -1,6 +1,7 @@ # Sources: set(arbor_sources + arbexcept.cpp assert.cpp backends/multicore/mechanism.cpp backends/multicore/shared_state.cpp @@ -35,6 +36,7 @@ set(arbor_sources profile/power_meter.cpp profile/profiler.cpp schedule.cpp + spike_event_io.cpp spike_source_cell_group.cpp swcio.cpp threadinfo.cpp diff --git a/arbor/arbexcept.cpp b/arbor/arbexcept.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a8b589b7b0c3490560f599b3df951f819e1c257a --- /dev/null +++ b/arbor/arbexcept.cpp @@ -0,0 +1,74 @@ +#include <string> +#include <sstream> + +#include <arbor/arbexcept.hpp> +#include <arbor/common_types.hpp> + +#include "util/strprintf.hpp" + +namespace arb { + +using arb::util::pprintf; + +bad_cell_description::bad_cell_description(cell_kind kind, cell_gid_type gid): + arbor_exception(pprintf("bad description for cell kind {} on gid {}", kind, gid)), + gid(gid), + kind(kind) +{} + +bad_global_property::bad_global_property(cell_kind kind): + arbor_exception(pprintf("bad global property for cell kind {}", kind)), + kind(kind) +{} + +bad_probe_id::bad_probe_id(cell_member_type probe_id): + arbor_exception(pprintf("bad probe id {}", probe_id)), + probe_id(probe_id) +{} + +bad_event_time::bad_event_time(time_type event_time, time_type sim_time): + arbor_exception(pprintf("event time {} precedes current simulation time {}", event_time, sim_time)), + event_time(event_time), + sim_time(sim_time) +{} + +no_such_mechanism::no_such_mechanism(const std::string& mech_name): + arbor_exception(pprintf("no mechanism {} in catalogue", mech_name)), + mech_name(mech_name) +{} + +duplicate_mechanism::duplicate_mechanism(const std::string& mech_name): + arbor_exception(pprintf("mechanism {} already exists", mech_name)), + mech_name(mech_name) +{} + +fingerprint_mismatch::fingerprint_mismatch(const std::string& mech_name): + arbor_exception(pprintf("mechanism {} has different fingerprint in schema", mech_name)), + mech_name(mech_name) +{} + +no_such_parameter::no_such_parameter(const std::string& mech_name, const std::string& param_name): + arbor_exception(pprintf("mechanism {} has no parameter {}", mech_name, param_name)), + mech_name(mech_name), + param_name(param_name) +{} + +invalid_parameter_value::invalid_parameter_value(const std::string& mech_name, const std::string& param_name, double value): + arbor_exception(pprintf("invalid parameter value for mechanism {} parameter {}: {}", mech_name, param_name, value)), + mech_name(mech_name), + param_name(param_name), + value(value) +{} + +no_such_implementation::no_such_implementation(const std::string& mech_name): + arbor_exception(pprintf("missing implementation for mechanism {} in catalogue", mech_name)), + mech_name(mech_name) +{} + +range_check_failure::range_check_failure(const std::string& whatstr, double value): + arbor_exception(pprintf("range check failure: {} with value {}", whatstr, value)), + value(value) +{} + +} // namespace arb + diff --git a/arbor/backends.hpp b/arbor/backends.hpp deleted file mode 100644 index e45f96e0bc2afe5d855f7db8b6905fc3a3015e96..0000000000000000000000000000000000000000 --- a/arbor/backends.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include <string> - -namespace arb { - -enum class backend_kind { - multicore, // use multicore backend for all computation - gpu // use gpu back end when supported by cell_group type -}; - -inline std::string to_string(backend_kind p) { - switch (p) { - case backend_kind::multicore: - return "multicore"; - case backend_kind::gpu: - return "gpu"; - } - return "unknown"; -} - -} // namespace arb diff --git a/arbor/backends/gpu/mechanism.cpp b/arbor/backends/gpu/mechanism.cpp index 8e6d7baeebbc75bb77247f69d1afdccb77dd071f..cb57c95ae07ae8af312aae225d54d01abe866f00 100644 --- a/arbor/backends/gpu/mechanism.cpp +++ b/arbor/backends/gpu/mechanism.cpp @@ -5,6 +5,7 @@ #include <utility> #include <vector> +#include <arbor/arbexcept.hpp> #include <arbor/common_types.hpp> #include <arbor/fvm_types.hpp> #include <arbor/mechanism.hpp> @@ -81,7 +82,7 @@ void mechanism::instantiate(unsigned id, for (auto i: ion_state_tbl) { util::optional<ion_state&> oion = value_by_key(shared.ion_data, i.first); if (!oion) { - throw std::logic_error("mechanism holds ion with no corresponding shared state"); + throw arbor_internal_error("gpu/mechanism: mechanism holds ion with no corresponding shared state"); } ion_state_view& ion_view = *i.second; @@ -132,7 +133,7 @@ void mechanism::instantiate(unsigned id, for (auto i: make_span(0, num_ions_)) { util::optional<ion_state&> oion = value_by_key(shared.ion_data, ion_index_tbl[i].first); if (!oion) { - throw std::logic_error("mechanism holds ion with no corresponding shared state"); + throw arbor_internal_error("gpu/mechanism: mechanism holds ion with no corresponding shared state"); } auto ni = memory::on_host(oion->node_index_); @@ -150,7 +151,7 @@ void mechanism::instantiate(unsigned id, void mechanism::set_parameter(const std::string& key, const std::vector<fvm_value_type>& values) { if (auto opt_ptr = value_by_key(field_table(), key)) { if (values.size()!=width_) { - throw std::logic_error("internal error: mechanism parameter size mismatch"); + throw arbor_internal_error("gpu/mechanism: mechanism parameter size mismatch"); } if (width_>0) { @@ -160,7 +161,7 @@ void mechanism::set_parameter(const std::string& key, const std::vector<fvm_valu } } else { - throw std::logic_error("internal error: no such mechanism parameter"); + throw arbor_internal_error("gpu/mechanism: no such mechanism parameter"); } } @@ -171,7 +172,7 @@ void mechanism::set_global(const std::string& key, fvm_value_type value) { global = value; } else { - throw std::logic_error("internal error: no such mechanism global"); + throw arbor_internal_error("gpu/mechanism: no such mechanism global"); } } diff --git a/arbor/backends/gpu/multi_event_stream.hpp b/arbor/backends/gpu/multi_event_stream.hpp index 4498b87cb9f208c76f32e5054bf886a48f09594e..b03e374c6f954e4aa18c7ad12386a2c7ca7efb84 100644 --- a/arbor/backends/gpu/multi_event_stream.hpp +++ b/arbor/backends/gpu/multi_event_stream.hpp @@ -4,10 +4,10 @@ #include <arbor/common_types.hpp> #include <arbor/fvm_types.hpp> +#include <arbor/generic_event.hpp> #include "backends/event.hpp" #include "backends/multi_event_stream_state.hpp" -#include "generic_event.hpp" #include "memory/array.hpp" #include "memory/copy.hpp" #include "profile/profiler_macro.hpp" @@ -68,7 +68,7 @@ protected: using ::arb::event_index; if (staged.size()>std::numeric_limits<size_type>::max()) { - throw std::range_error("too many events"); + throw arbor_internal_error("gpu/multi_event_stream: too many events for size type"); } arb_assert(util::is_sorted_by(staged, [](const Event& ev) { return event_index(ev); })); diff --git a/arbor/backends/gpu/threshold_watcher.hpp b/arbor/backends/gpu/threshold_watcher.hpp index 09f5627561018e51a9c4c0201cc2958456842b02..7cc62f8a0f25dccf7818d563f2e220504e006a5c 100644 --- a/arbor/backends/gpu/threshold_watcher.hpp +++ b/arbor/backends/gpu/threshold_watcher.hpp @@ -1,5 +1,6 @@ #pragma once +#include <arbor/arbexcept.hpp> #include <arbor/common_types.hpp> #include <arbor/fvm_types.hpp> @@ -88,7 +89,7 @@ public: stack_.host_access(); if (stack_.overflow()) { - throw std::runtime_error("GPU spike buffer overflow."); + throw arbor_internal_error("gpu/threshold_watcher: gpu spike buffer overflow"); } crossings_.clear(); diff --git a/arbor/backends/multicore/mechanism.cpp b/arbor/backends/multicore/mechanism.cpp index 3fbb4fb1a81238be8531061c980d962208387fb8..a5b22f3e54631b3e5c75e1df5ae26185aed5635e 100644 --- a/arbor/backends/multicore/mechanism.cpp +++ b/arbor/backends/multicore/mechanism.cpp @@ -83,7 +83,7 @@ void mechanism::instantiate(unsigned id, backend::shared_state& shared, const la for (auto i: ion_state_tbl) { util::optional<ion_state&> oion = value_by_key(shared.ion_data, i.first); if (!oion) { - throw std::logic_error("mechanism holds ion with no corresponding shared state"); + throw arbor_internal_error("multicore/mechanism: mechanism holds ion with no corresponding shared state"); } ion_state_view& ion_view = *i.second; @@ -136,7 +136,7 @@ void mechanism::instantiate(unsigned id, backend::shared_state& shared, const la for (auto i: ion_index_table()) { util::optional<ion_state&> oion = value_by_key(shared.ion_data, i.first); if (!oion) { - throw std::logic_error("mechanism holds ion with no corresponding shared state"); + throw arbor_internal_error("multicore/mechanism: mechanism holds ion with no corresponding shared state"); } auto indices = util::index_into(node_index_, oion->node_index_); @@ -154,7 +154,7 @@ void mechanism::instantiate(unsigned id, backend::shared_state& shared, const la void mechanism::set_parameter(const std::string& key, const std::vector<fvm_value_type>& values) { if (auto opt_ptr = value_by_key(field_table(), key)) { if (values.size()!=width_) { - throw std::logic_error("internal error: mechanism parameter size mismatch"); + throw arbor_internal_error("multicore/mechanism: mechanism parameter size mismatch"); } if (width_>0) { @@ -166,7 +166,7 @@ void mechanism::set_parameter(const std::string& key, const std::vector<fvm_valu } } else { - throw std::logic_error("internal error: no such mechanism parameter"); + throw arbor_internal_error("multicore/mechanism: no such mechanism parameter"); } } @@ -177,7 +177,7 @@ void mechanism::set_global(const std::string& key, fvm_value_type value) { global = value; } else { - throw std::logic_error("internal error: no such mechanism global"); + throw arbor_internal_error("multicore/mechanism: no such mechanism global"); } } diff --git a/arbor/backends/multicore/multi_event_stream.hpp b/arbor/backends/multicore/multi_event_stream.hpp index 2a761b8c7cb6d218c10eee7f0b354bd1669fbade..aa0bea06b1095c38bb937e9b1d0b3a3feb60fb54 100644 --- a/arbor/backends/multicore/multi_event_stream.hpp +++ b/arbor/backends/multicore/multi_event_stream.hpp @@ -7,11 +7,12 @@ #include <utility> #include <arbor/assert.hpp> +#include <arbor/arbexcept.hpp> #include <arbor/fvm_types.hpp> +#include <arbor/generic_event.hpp> #include "backends/event.hpp" #include "backends/multi_event_stream_state.hpp" -#include "generic_event.hpp" #include "algorithms.hpp" #include "util/range.hpp" #include "util/rangeutil.hpp" @@ -58,7 +59,7 @@ public: using ::arb::event_data; if (staged.size()>std::numeric_limits<size_type>::max()) { - throw std::range_error("too many events"); + throw arbor_internal_error("multicore/multi_event_stream: too many events for size type"); } // Sort by index (staged events should already be time-sorted). diff --git a/arbor/benchmark_cell_group.cpp b/arbor/benchmark_cell_group.cpp index 6466d7c1b31fb9027ff9e43054b8d8925d3009c5..0556045dea9a2abbd5afa1ad9c6beae64bd5e073 100644 --- a/arbor/benchmark_cell_group.cpp +++ b/arbor/benchmark_cell_group.cpp @@ -2,12 +2,12 @@ #include <exception> #include <arbor/benchmark_cell.hpp> +#include <arbor/recipe.hpp> #include <arbor/time_sequence.hpp> -#include <cell_group.hpp> -#include <profile/profiler_macro.hpp> -#include <recipe.hpp> -#include <benchmark_cell_group.hpp> +#include "cell_group.hpp" +#include "profile/profiler_macro.hpp" +#include "benchmark_cell_group.hpp" #include "util/span.hpp" diff --git a/arbor/benchmark_cell_group.hpp b/arbor/benchmark_cell_group.hpp index 146baf7fc48701f5a42f5124d3bf3f90f665cd2b..02fee8d51758043616848e4398e149e81ad21008 100644 --- a/arbor/benchmark_cell_group.hpp +++ b/arbor/benchmark_cell_group.hpp @@ -1,10 +1,13 @@ #pragma once #include <arbor/benchmark_cell.hpp> -#include <arbor/time_sequence.hpp> +#include <arbor/common_types.hpp> +#include <arbor/recipe.hpp> +#include <arbor/sampling.hpp> +#include <arbor/spike.hpp> #include "cell_group.hpp" -#include "recipe.hpp" +#include "epoch.hpp" namespace arb { diff --git a/arbor/cell_group.hpp b/arbor/cell_group.hpp index ed53924f48055d9d9999112b7d9e2d4a0d3baacd..98cf20ab102d43dcce888f76408b724386733f89 100644 --- a/arbor/cell_group.hpp +++ b/arbor/cell_group.hpp @@ -6,15 +6,19 @@ #include <arbor/common_types.hpp> #include <arbor/sampling.hpp> +#include <arbor/schedule.hpp> #include <arbor/spike.hpp> +#include <arbor/spike_event.hpp> #include "epoch.hpp" #include "event_binner.hpp" #include "event_queue.hpp" -#include "schedule.hpp" +#include "util/rangeutil.hpp" namespace arb { +using event_lane_subrange = util::subrange_view_type<std::vector<pse_vector>>; + class cell_group { public: virtual ~cell_group() = default; diff --git a/arbor/cell_group_factory.cpp b/arbor/cell_group_factory.cpp index 96079522d497f566a9048ba08bab17515b323ec1..54e343eaf3a04e565df3949d3ac50c495033112b 100644 --- a/arbor/cell_group_factory.cpp +++ b/arbor/cell_group_factory.cpp @@ -1,14 +1,16 @@ #include <vector> -#include <backends.hpp> -#include <benchmark_cell_group.hpp> -#include <cell_group.hpp> -#include <domain_decomposition.hpp> -#include <fvm_lowered_cell.hpp> -#include <lif_cell_group.hpp> -#include <mc_cell_group.hpp> -#include <recipe.hpp> -#include <spike_source_cell_group.hpp> +#include <arbor/arbexcept.hpp> +#include <arbor/common_types.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> + +#include "benchmark_cell_group.hpp" +#include "cell_group.hpp" +#include "fvm_lowered_cell.hpp" +#include "lif_cell_group.hpp" +#include "mc_cell_group.hpp" +#include "spike_source_cell_group.hpp" namespace arb { @@ -27,7 +29,7 @@ cell_group_ptr cell_group_factory(const recipe& rec, const group_description& gr return make_cell_group<benchmark_cell_group>(group.gids, rec); default: - throw std::runtime_error("unknown cell kind"); + throw arbor_internal_error("cell_group_factory: unknown cell kind"); } } diff --git a/arbor/cell_group_factory.hpp b/arbor/cell_group_factory.hpp index 88770475dc11d8faa392fe67cbd3f7b92818c866..320e4ff531aad22074cee94d569bc5a9d9c60942 100644 --- a/arbor/cell_group_factory.hpp +++ b/arbor/cell_group_factory.hpp @@ -1,13 +1,9 @@ #pragma once -#include <vector> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> -#include <arbor/util/unique_any.hpp> - -#include "backends.hpp" #include "cell_group.hpp" -#include "domain_decomposition.hpp" -#include "recipe.hpp" namespace arb { diff --git a/arbor/common_types_io.cpp b/arbor/common_types_io.cpp index 2e46ddd7d301474f098cd2650992011b1d79d4af..c5b904784f700af08d78c8805a04728cf096001d 100644 --- a/arbor/common_types_io.cpp +++ b/arbor/common_types_io.cpp @@ -2,8 +2,10 @@ #include <arbor/common_types.hpp> -std::ostream& operator<<(std::ostream& O, arb::cell_member_type m) { - return O << m.gid << ':' << m.index; +namespace arb { + +std::ostream& operator<<(std::ostream& o, arb::cell_member_type m) { + return o << m.gid << ':' << m.index; } std::ostream& operator<<(std::ostream& o, arb::cell_kind k) { @@ -21,3 +23,15 @@ std::ostream& operator<<(std::ostream& o, arb::cell_kind k) { return o; } +std::ostream& operator<<(std::ostream& o, arb::backend_kind k) { + o << "backend_kind::"; + switch (k) { + case arb::backend_kind::multicore: + return o << "multicore"; + case arb::backend_kind::gpu: + return o << "gpu"; + } + return o; +} + +} diff --git a/arbor/communication/communicator.hpp b/arbor/communication/communicator.hpp index d9a6f75935f93e82448e93210b79d7db8c37a2f4..78aa7e4c6d2b38d159d866154f49d51c75dc004d 100644 --- a/arbor/communication/communicator.hpp +++ b/arbor/communication/communicator.hpp @@ -11,14 +11,14 @@ #include <arbor/common_types.hpp> #include <arbor/communication/gathered_vector.hpp> #include <arbor/distributed_context.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> #include <arbor/spike.hpp> #include "algorithms.hpp" #include "connection.hpp" -#include "domain_decomposition.hpp" #include "event_queue.hpp" #include "profile/profiler_macro.hpp" -#include "recipe.hpp" #include "threading/threading.hpp" #include "util/double_buffer.hpp" #include "util/partition.hpp" diff --git a/arbor/connection.hpp b/arbor/connection.hpp index 40d19287763395d9513cd82caaaf5a5f4b080595..e4fbf1509668dca58b11cd18dd91822c855260e8 100644 --- a/arbor/connection.hpp +++ b/arbor/connection.hpp @@ -31,7 +31,7 @@ public: cell_member_type destination() const { return destination_; } cell_size_type index_on_domain() const { return index_on_domain_; } - postsynaptic_spike_event make_event(const spike& s) { + spike_event make_event(const spike& s) { return {destination_, s.time + delay_, weight_}; } diff --git a/arbor/event_binner.cpp b/arbor/event_binner.cpp index 7fcdbbac4f790b41ef5021d17d526755f95c0ee7..6301a3499d84d0380a8ba5a7eea72c4db24994a9 100644 --- a/arbor/event_binner.cpp +++ b/arbor/event_binner.cpp @@ -4,6 +4,7 @@ #include <stdexcept> #include <unordered_map> +#include <arbor/arbexcept.hpp> #include <arbor/common_types.hpp> #include <arbor/spike.hpp> #include <arbor/util/optional.hpp> @@ -36,7 +37,7 @@ time_type event_binner::bin(time_type t, time_type t_min) { last_event_time_ = t_binned; break; default: - throw std::logic_error("unrecognized binning policy"); + throw arbor_internal_error("event_binner: unrecognized binning policy"); } return std::max(t_binned, t_min); diff --git a/arbor/event_binner.hpp b/arbor/event_binner.hpp index 25ff4f0bf444fe1da659a9fe8b67b63eb83186f9..c1bcd72c5147972a77a258805e26da8ce8a1c8a4 100644 --- a/arbor/event_binner.hpp +++ b/arbor/event_binner.hpp @@ -9,12 +9,6 @@ namespace arb { -enum class binning_kind { - none, - regular, // => round time down to multiple of binning interval. - following, // => round times down to previous event if within binning interval. -}; - class event_binner { public: event_binner(): policy_(binning_kind::none), bin_interval_(0) {} diff --git a/arbor/event_queue.hpp b/arbor/event_queue.hpp index 0a5a24d9e6397c3eb2a6c4ca869a23e1d922f909..b785acd06299d6a3fb60e93d1afa50013e98793c 100644 --- a/arbor/event_queue.hpp +++ b/arbor/event_queue.hpp @@ -8,13 +8,9 @@ #include <utility> #include <arbor/common_types.hpp> +#include <arbor/spike_event.hpp> #include <arbor/util/optional.hpp> - -#include "generic_event.hpp" -#include "util/meta.hpp" -#include "util/range.hpp" -#include "util/rangeutil.hpp" -#include "util/strprintf.hpp" +#include <arbor/generic_event.hpp> namespace arb { @@ -25,31 +21,9 @@ namespace arb { * Time values must be well ordered with respect to `operator>`. */ -struct postsynaptic_spike_event { - cell_member_type target; - time_type time; - float weight; - - friend bool operator==(const postsynaptic_spike_event& l, const postsynaptic_spike_event& r) { - return l.target==r.target && l.time==r.time && l.weight==r.weight; - } - - friend bool operator<(const postsynaptic_spike_event& l, const postsynaptic_spike_event& r) { - return std::tie(l.time, l.target, l.weight) < std::tie(r.time, r.target, r.weight); - } - - friend std::ostream& operator<<(std::ostream& o, const arb::postsynaptic_spike_event& e) - { - return o << "E[tgt " << e.target << ", t " << e.time << ", w " << e.weight << "]"; - } -}; - -using pse_vector = std::vector<postsynaptic_spike_event>; -using event_lane_subrange = util::subrange_view_type<std::vector<pse_vector>>; - template <typename Event> class event_queue { -public : +public: using value_type = Event; using event_time_type = ::arb::event_time_type<Event>; diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp index d634fc711172bc8549fc4785ee37d1e82f3c4467..6f3d1ee9c10a253c48c30bc898437deae242b595 100644 --- a/arbor/fvm_layout.cpp +++ b/arbor/fvm_layout.cpp @@ -3,6 +3,7 @@ #include <unordered_set> #include <vector> +#include <arbor/arbexcept.hpp> #include <arbor/mc_cell.hpp> #include <arbor/util/enumhash.hpp> @@ -156,16 +157,16 @@ fvm_discretization fvm_discretize(const std::vector<mc_cell>& cells) { const auto nseg = seg_comp_part.size(); if (nseg==0) { - throw std::invalid_argument("cannot discretrize cell with no segments"); + throw arbor_internal_error("fvm_layout: cannot discretrize cell with no segments"); } // Handle soma (first segment and root of tree) specifically. const auto soma = c.segment(0)->as_soma(); if (!soma) { - throw std::logic_error("First segment of cell must be soma"); + throw arbor_internal_error("fvm_layout: first segment of cell must be soma"); } else if (soma->num_compartments()!=1) { - throw std::logic_error("Soma must have exactly one compartment"); + throw arbor_internal_error("fvm_layout: soma must have exactly one compartment"); } segment_info soma_info; @@ -190,7 +191,7 @@ fvm_discretization fvm_discretize(const std::vector<mc_cell>& cells) { const auto cable = c.segment(j)->as_cable(); if (!cable) { - throw std::logic_error("Non-root segments of cell must be cable segments"); + throw arbor_internal_error("fvm_layout: non-root segments of cell must be cable segments"); } auto cm = cable->cm; // [F/m²] auto rL = cable->rL; // [Ω·cm] @@ -309,18 +310,15 @@ fvm_mechanism_data fvm_build_mechanism_data(const mechanism_catalogue& catalogue { auto& name = desc.name(); if (!info) { - if (!catalogue.has(name)) { - throw std::out_of_range("No mechanism "+name+" in mechanism catalogue"); - } info = &catalogue[name]; } for (const auto& pv: desc.values()) { if (!paramset.count(pv.first)) { if (!info->parameters.count(pv.first)) { - throw std::out_of_range("Mechanism "+name+" has no parameter "+pv.first); + throw no_such_parameter(name, pv.first); } if (!info->parameters.at(pv.first).valid(pv.second)) { - throw std::out_of_range("Value out of range for mechanism "+name+" parameter "+pv.first); + throw invalid_parameter_value(name, pv.first, pv.second); } paramset.insert(pv.first); } diff --git a/arbor/fvm_lowered_cell.hpp b/arbor/fvm_lowered_cell.hpp index 3b22c5ffb8a5618bc126b5c8da8abdb2316dd498..81a2688793d87c2ceaf26e455398582e74e18d53 100644 --- a/arbor/fvm_lowered_cell.hpp +++ b/arbor/fvm_lowered_cell.hpp @@ -3,14 +3,14 @@ #include <memory> #include <vector> +#include <arbor/common_types.hpp> #include <arbor/fvm_types.hpp> +#include <arbor/recipe.hpp> -#include <backends.hpp> -#include <backends/event.hpp> -#include <backends/threshold_crossing.hpp> -#include <recipe.hpp> -#include <sampler_map.hpp> -#include <util/range.hpp> +#include "backends/event.hpp" +#include "backends/threshold_crossing.hpp" +#include "sampler_map.hpp" +#include "util/range.hpp" namespace arb { diff --git a/arbor/fvm_lowered_cell_impl.cpp b/arbor/fvm_lowered_cell_impl.cpp index fe317e93b18ec909dd33e92dffcd32ca4d4106b3..1136f64e146eb7c3c8581644b3637afb2b3fbc62 100644 --- a/arbor/fvm_lowered_cell_impl.cpp +++ b/arbor/fvm_lowered_cell_impl.cpp @@ -1,7 +1,9 @@ #include <memory> #include <stdexcept> -#include "backends.hpp" +#include <arbor/arbexcept.hpp> +#include <arbor/common_types.hpp> + #include "backends/multicore/fvm.hpp" #ifdef ARB_HAVE_GPU #include "backends/gpu/fvm.hpp" @@ -20,7 +22,7 @@ fvm_lowered_cell_ptr make_fvm_lowered_cell(backend_kind p) { #endif ; // fall through default: - throw std::logic_error("unsupported back-end"); + throw arbor_internal_error("fvm_lowered_cell: unsupported back-end"); } } diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp index f8a7f6b9682c5a29b2b047c7d0bb9f502de5454a..b07fcfea27ab85a76ded70eeec67199f34297d00 100644 --- a/arbor/fvm_lowered_cell_impl.hpp +++ b/arbor/fvm_lowered_cell_impl.hpp @@ -16,18 +16,19 @@ #include <arbor/assert.hpp> #include <arbor/common_types.hpp> #include <arbor/ion.hpp> +#include <arbor/recipe.hpp> #include "builtin_mechanisms.hpp" #include "fvm_layout.hpp" #include "fvm_lowered_cell.hpp" #include "matrix.hpp" #include "profile/profiler_macro.hpp" -#include "recipe.hpp" #include "sampler_map.hpp" #include "util/maputil.hpp" #include "util/meta.hpp" #include "util/range.hpp" #include "util/rangeutil.hpp" +#include "util/strprintf.hpp" #include "util/transform.hpp" @@ -113,10 +114,10 @@ template <typename Backend> void fvm_lowered_cell_impl<Backend>::assert_tmin() { auto time_minmax = state_->time_bounds(); if (time_minmax.first != time_minmax.second) { - throw std::logic_error("inconsistent times across cells"); + throw arbor_internal_error("fvm_lowered_cell: inconsistent times across cells"); } if (time_minmax.first != tmin_) { - throw std::logic_error("out of synchronziation with cell state time"); + throw arbor_internal_error("fvm_lowered_cell: out of synchronziation with cell state time"); } } @@ -276,8 +277,9 @@ void fvm_lowered_cell_impl<B>::assert_voltage_bounded(fvm_value_type bound) { } auto t_minmax = state_->time_bounds(); - throw std::out_of_range("voltage solution out of bounds for t in ["+ - std::to_string(t_minmax.first)+", "+std::to_string(t_minmax.second)+"]"); + throw range_check_failure( + util::pprintf("voltage solution out of bounds for t in [{}, {}]", t_minmax.first, t_minmax.second), + v_minmax.first<-bound? v_minmax.first: v_minmax.second); } template <typename B> @@ -298,11 +300,24 @@ void fvm_lowered_cell_impl<B>::initialize( cells.reserve(ncell); for (auto gid: gids) { - cells.push_back(any_cast<mc_cell>(rec.get_cell_description(gid))); + try { + cells.push_back(any_cast<mc_cell>(rec.get_cell_description(gid))); + } + catch (util::bad_any_cast&) { + throw bad_cell_description(rec.get_cell_kind(gid), gid); + } } - auto rec_props = rec.get_global_properties(cell_kind::cable1d_neuron); - auto global_props = rec_props.has_value()? any_cast<mc_cell_global_properties>(rec_props): mc_cell_global_properties{}; + mc_cell_global_properties global_props; + try { + util::any rec_props = rec.get_global_properties(cell_kind::cable1d_neuron); + if (rec_props.has_value()) { + global_props = any_cast<mc_cell_global_properties>(rec_props); + } + } + catch (util::bad_any_cast&) { + throw bad_global_property(cell_kind::cable1d_neuron); + } const mechanism_catalogue* catalogue = global_props.catalogue; initial_voltage_ = global_props.init_membrane_potential_mV; @@ -423,7 +438,7 @@ void fvm_lowered_cell_impl<B>::initialize( handle = state_->current_density.data()+cv; break; default: - throw std::logic_error("unrecognized probeKind"); + throw arbor_internal_error("fvm_lowered_cell: unrecognized probeKind"); } probe_map.insert({pi.id, {handle, pi.tag}}); diff --git a/arbor/lif_cell_group.cpp b/arbor/lif_cell_group.cpp index 70f51250471aae1a018db4e4b628145dc62909ea..7cce6cc5ed11a0e9f4451ba7608595d4374573f5 100644 --- a/arbor/lif_cell_group.cpp +++ b/arbor/lif_cell_group.cpp @@ -1,5 +1,6 @@ #include <lif_cell_group.hpp> +#include "profile/profiler_macro.hpp" #include "util/span.hpp" using namespace arb; diff --git a/arbor/lif_cell_group.hpp b/arbor/lif_cell_group.hpp index d81aaf3b3094c5158377e2fdb4d2eb954c8ce9ab..6ed49d39ba94931419e5a19237d957953cb5e49d 100644 --- a/arbor/lif_cell_group.hpp +++ b/arbor/lif_cell_group.hpp @@ -2,12 +2,13 @@ #include <vector> +#include <arbor/common_types.hpp> #include <arbor/lif_cell.hpp> +#include <arbor/recipe.hpp> +#include <arbor/sampling.hpp> +#include <arbor/spike.hpp> #include "cell_group.hpp" -#include "event_queue.hpp" -#include "profile/profiler_macro.hpp" -#include "recipe.hpp" namespace arb { diff --git a/arbor/load_balance.hpp b/arbor/load_balance.hpp index 0dbb0cf4b86b477da691714a6996e249a41c9d83..757f5a31422d673a3b781ed2b6de9ea1a98b4d3e 100644 --- a/arbor/load_balance.hpp +++ b/arbor/load_balance.hpp @@ -1,8 +1,10 @@ +#pragma once + #include <arbor/distributed_context.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> -#include "domain_decomposition.hpp" #include "hardware/node_info.hpp" -#include "recipe.hpp" namespace arb { diff --git a/arbor/mc_cell.cpp b/arbor/mc_cell.cpp index 2184134c5f62b52006b1c9ee2cf33e851523604a..5807afd0dafa9f6b26ed783392c90b8f58b42c33 100644 --- a/arbor/mc_cell.cpp +++ b/arbor/mc_cell.cpp @@ -18,7 +18,7 @@ mc_cell::mc_cell() { void mc_cell::assert_valid_segment(index_type i) const { if (i>=num_segments()) { - throw std::out_of_range("no such segment"); + throw mc_cell_error("no such segment"); } } @@ -32,7 +32,7 @@ size_type mc_cell::num_segments() const { // soma_segment* mc_cell::add_soma(value_type radius, point_type center) { if (has_soma()) { - throw std::runtime_error("cell already has soma"); + throw mc_cell_error("cell already has soma"); } segments_[0] = make_segment<soma_segment>(radius, center); return segments_[0]->as_soma(); @@ -40,11 +40,11 @@ soma_segment* mc_cell::add_soma(value_type radius, point_type center) { cable_segment* mc_cell::add_cable(index_type parent, mc_segment_ptr&& cable) { if (!cable->as_cable()) { - throw std::invalid_argument("segment is not a cable segment"); + throw mc_cell_error("segment is not a cable segment"); } if (parent>num_segments()) { - throw std::out_of_range("parent index out of range"); + throw mc_cell_error("parent index out of range"); } segments_.push_back(std::move(cable)); @@ -78,7 +78,7 @@ const soma_segment* mc_cell::soma() const { cable_segment* mc_cell::cable(index_type index) { assert_valid_segment(index); auto cable = segment(index)->as_cable(); - return cable? cable: throw std::runtime_error("segment is not a cable segment"); + return cable? cable: throw mc_cell_error("segment is not a cable segment"); } std::vector<size_type> mc_cell::compartment_counts() const { @@ -127,7 +127,7 @@ mc_cell make_mc_cell(const morphology& morph, bool compartments_from_discretizat kind = section_kind::dendrite; break; case section_kind::soma: - throw std::invalid_argument("no support for complex somata"); + throw mc_cell_error("no support for complex somata"); break; default: ; } diff --git a/arbor/mc_cell_group.cpp b/arbor/mc_cell_group.cpp index b5e12cc263bdd6676b15d77061e704a3b7c057af..e03ae795d05a0cd93d6e71050edcc20323add82a 100644 --- a/arbor/mc_cell_group.cpp +++ b/arbor/mc_cell_group.cpp @@ -5,6 +5,7 @@ #include <arbor/assert.hpp> #include <arbor/common_types.hpp> #include <arbor/sampling.hpp> +#include <arbor/recipe.hpp> #include <arbor/spike.hpp> #include "backends/event.hpp" @@ -14,7 +15,6 @@ #include "fvm_lowered_cell.hpp" #include "mc_cell_group.hpp" #include "profile/profiler_macro.hpp" -#include "recipe.hpp" #include "sampler_map.hpp" #include "util/filter.hpp" #include "util/maputil.hpp" diff --git a/arbor/mc_cell_group.hpp b/arbor/mc_cell_group.hpp index 463e59d09c00e31066b565f99a4fd01f9cc3b036..be9599d75e5264335237be721b3d36fdffd8fe1c 100644 --- a/arbor/mc_cell_group.hpp +++ b/arbor/mc_cell_group.hpp @@ -6,18 +6,18 @@ #include <unordered_map> #include <vector> -#include <arbor/assert.hpp> #include <arbor/common_types.hpp> +#include <arbor/recipe.hpp> #include <arbor/sampling.hpp> #include <arbor/spike.hpp> #include "backends/event.hpp" #include "cell_group.hpp" +#include "epoch.hpp" #include "event_binner.hpp" #include "event_queue.hpp" #include "fvm_lowered_cell.hpp" #include "profile/profiler_macro.hpp" -#include "recipe.hpp" #include "sampler_map.hpp" #include "util/double_buffer.hpp" #include "util/filter.hpp" diff --git a/arbor/mechcat.cpp b/arbor/mechcat.cpp index 5ce8688407fe6ded889f1b4b3ec63b971aead6a1..ed314a78efe9cea2857fd9f84a675cd731e6b0ef 100644 --- a/arbor/mechcat.cpp +++ b/arbor/mechcat.cpp @@ -3,6 +3,7 @@ #include <string> #include <vector> +#include <arbor/arbexcept.hpp> #include <arbor/mechcat.hpp> #include <arbor/util/make_unique.hpp> @@ -15,7 +16,7 @@ using util::make_unique; void mechanism_catalogue::add(const std::string& name, mechanism_info info) { if (has(name)) { - throw std::invalid_argument("mechanism '"+name+"' already exists in catalogue"); + throw duplicate_mechanism(name); } info_map_[name] = mechanism_info_ptr(new mechanism_info(std::move(info))); @@ -29,7 +30,7 @@ const mechanism_info& mechanism_catalogue::operator[](const std::string& name) c return *(p->get()); } - throw std::invalid_argument("no mechanism with name +'"+name+"' in catalogue"); + throw no_such_mechanism(name); } const mechanism_fingerprint& mechanism_catalogue::fingerprint(const std::string& name) const { @@ -42,16 +43,16 @@ const mechanism_fingerprint& mechanism_catalogue::fingerprint(const std::string& return p.value()->fingerprint; } - throw std::invalid_argument("no mechanism with name +'"+name+"' in catalogue"); + throw no_such_mechanism(name); } void mechanism_catalogue::derive(const std::string& name, const std::string& parent, const std::vector<std::pair<std::string, double>>& global_params) { if (has(name)) { - throw std::invalid_argument("mechanism with name '"+name+"' already exists in catalogue"); + throw duplicate_mechanism(name); } if (!has(parent)) { - throw std::invalid_argument("no mechanism with name '"+parent+"' in catalogue"); + throw no_such_mechanism(parent); } derivation deriv = {parent, {}, nullptr}; @@ -63,11 +64,11 @@ void mechanism_catalogue::derive(const std::string& name, const std::string& par if (auto p = value_by_key(info->globals, param)) { if (!p->valid(value)) { - throw std::invalid_argument("invalid value for parameter '"+param+"' in mechanism '"+name+"'"); + throw invalid_parameter_value(name, param, value); } } else { - throw std::invalid_argument("mechanism '"+name+"' has no global parameter '"+param+"'"); + throw no_such_parameter(name, param); } deriv.globals[param] = value; @@ -80,7 +81,7 @@ void mechanism_catalogue::derive(const std::string& name, const std::string& par void mechanism_catalogue::remove(const std::string& name) { if (!has(name)) { - throw std::invalid_argument("no mechanism with name '"+name+"' in catalogue"); + throw no_such_mechanism(name); } if (is_derived(name)) { @@ -127,7 +128,7 @@ std::unique_ptr<mechanism> mechanism_catalogue::instance_impl(std::type_index ti impl_name = p->parent; } else { - throw std::invalid_argument("missing implementation for mechanism named '"+name+"'"); + throw no_such_implementation(name); } } @@ -153,7 +154,7 @@ void mechanism_catalogue::register_impl(std::type_index tidx, const std::string& const mechanism_info& info = (*this)[name]; if (mech->fingerprint()!=info.fingerprint) { - throw std::invalid_argument("implementation fingerprint does not match schema"); + throw fingerprint_mismatch(name); } impl_map_[name][tidx] = std::move(mech); diff --git a/arbor/merge_events.cpp b/arbor/merge_events.cpp index 4e5d8600c010cf4f9aabd1f23508d83fb5ac847e..096dd16965c0311fe7254052847b80500f4c8358 100644 --- a/arbor/merge_events.cpp +++ b/arbor/merge_events.cpp @@ -2,12 +2,13 @@ #include <set> #include <vector> -#include "backends.hpp" +#include <arbor/common_types.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> + #include "cell_group.hpp" #include "cell_group_factory.hpp" -#include "domain_decomposition.hpp" #include "merge_events.hpp" -#include "recipe.hpp" #include "util/filter.hpp" #include "util/span.hpp" #include "profile/profiler_macro.hpp" @@ -73,7 +74,7 @@ bool tourney_tree::empty(time_type t) const { return event(0).time >= t; } -postsynaptic_spike_event tourney_tree::head() const { +spike_event tourney_tree::head() const { return event(0); } @@ -130,10 +131,10 @@ bool tourney_tree::is_leaf(unsigned i) const { const unsigned& tourney_tree::id(unsigned i) const { return heap_[i].first; } -postsynaptic_spike_event& tourney_tree::event(unsigned i) { +spike_event& tourney_tree::event(unsigned i) { return heap_[i].second; } -const postsynaptic_spike_event& tourney_tree::event(unsigned i) const { +const spike_event& tourney_tree::event(unsigned i) const { return heap_[i].second; } diff --git a/arbor/merge_events.hpp b/arbor/merge_events.hpp index 72c3918d8d61543b5b14ae3c482dd5af4e92ff53..eb058fec6c92200c6d5e0ca17bb3b9db1c972af9 100644 --- a/arbor/merge_events.hpp +++ b/arbor/merge_events.hpp @@ -1,11 +1,12 @@ #pragma once -#include <algorithm> #include <iosfwd> #include <vector> -#include <event_generator.hpp> -#include <event_queue.hpp> +#include <arbor/event_generator.hpp> +#include <arbor/spike_event.hpp> + +#include "event_queue.hpp" #include "profile/profiler_macro.hpp" namespace arb { @@ -49,13 +50,13 @@ namespace impl { // it is not intended for use elsewhere. It is exposed here for unit testing // of its functionality. class tourney_tree { - using key_val = std::pair<unsigned, postsynaptic_spike_event>; + using key_val = std::pair<unsigned, spike_event>; public: tourney_tree(std::vector<event_generator>& input); bool empty() const; bool empty(time_type t) const; - postsynaptic_spike_event head() const; + spike_event head() const; void pop(); friend std::ostream& operator<<(std::ostream&, const tourney_tree&); @@ -69,8 +70,8 @@ namespace impl { unsigned leaf(unsigned i) const; bool is_leaf(unsigned i) const; const unsigned& id(unsigned i) const; - postsynaptic_spike_event& event(unsigned i); - const postsynaptic_spike_event& event(unsigned i) const; + spike_event& event(unsigned i); + const spike_event& event(unsigned i) const; unsigned next_power_2(unsigned x) const; std::vector<key_val> heap_; diff --git a/arbor/partition_load_balance.cpp b/arbor/partition_load_balance.cpp index 062373de76450935d0c1153c6da7e67526845621..c9a18ec23f07728b002a5563f6050970faaad941 100644 --- a/arbor/partition_load_balance.cpp +++ b/arbor/partition_load_balance.cpp @@ -1,9 +1,10 @@ #include <arbor/distributed_context.hpp> #include <arbor/util/enumhash.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> -#include "domain_decomposition.hpp" #include "hardware/node_info.hpp" -#include "recipe.hpp" +#include "util/partition.hpp" #include "util/span.hpp" namespace arb { diff --git a/arbor/sampler_map.hpp b/arbor/sampler_map.hpp index 08040015a814b1fa50a025c6fe68282067b57131..5d137fbfb14851f1240ac0550767231d5b0d7a31 100644 --- a/arbor/sampler_map.hpp +++ b/arbor/sampler_map.hpp @@ -11,8 +11,8 @@ #include <arbor/common_types.hpp> #include <arbor/sampling.hpp> +#include <arbor/schedule.hpp> -#include "schedule.hpp" #include "util/deduce_return.hpp" #include "util/transform.hpp" diff --git a/arbor/schedule.cpp b/arbor/schedule.cpp index 18880296422e2d9e4ec4d10268e41d0263c9e52c..2bfc246e91ca2592c163bea6fa081942e2c29ebe 100644 --- a/arbor/schedule.cpp +++ b/arbor/schedule.cpp @@ -4,8 +4,7 @@ #include <vector> #include <arbor/common_types.hpp> - -#include "schedule.hpp" +#include <arbor/schedule.hpp> // Implementations for specific schedules. diff --git a/arbor/simulation.cpp b/arbor/simulation.cpp index 5134e714d6c99fa0b62f9a7678937161ccbf2ae5..8339b0c05985b4fd2bc278bd1a4ab3dcf54df800 100644 --- a/arbor/simulation.cpp +++ b/arbor/simulation.cpp @@ -2,16 +2,20 @@ #include <set> #include <vector> -#include "backends.hpp" +#include <arbor/recipe.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/schedule.hpp> +#include <arbor/simulation.hpp> + #include "cell_group.hpp" #include "cell_group_factory.hpp" -#include "domain_decomposition.hpp" +#include "communication/communicator.hpp" #include "merge_events.hpp" -#include "simulation.hpp" -#include "recipe.hpp" #include "thread_private_spike_store.hpp" #include "util/double_buffer.hpp" #include "util/filter.hpp" +#include "util/maputil.hpp" +#include "util/partition.hpp" #include "util/span.hpp" #include "profile/profiler_macro.hpp" @@ -38,9 +42,88 @@ public: void exchange() { buffer_.exchange(); } }; -simulation::simulation(const recipe& rec, - const domain_decomposition& decomp, - const distributed_context* ctx): +class simulation_state { +public: + simulation_state(const recipe& rec, const domain_decomposition& decomp, const distributed_context* ctx); + + void reset(); + + time_type run(time_type tfinal, time_type dt); + + sampler_association_handle add_sampler(cell_member_predicate probe_ids, + schedule sched, sampler_function f, sampling_policy policy = sampling_policy::lax); + + void remove_sampler(sampler_association_handle); + + void remove_all_samplers(); + + std::size_t num_spikes() const { + return communicator_.num_spikes(); + } + + void set_binning_policy(binning_kind policy, time_type bin_interval); + + void inject_events(const pse_vector& events); + + spike_export_function global_export_callback_; + spike_export_function local_export_callback_; + +private: + // Private helper function that sets up the event lanes for an epoch. + // See comments on implementation for more information. + void setup_events(time_type t_from, time_type time_to, std::size_t epoch_id); + + std::vector<pse_vector>& event_lanes(std::size_t epoch_id) { + return event_lanes_[epoch_id%2]; + } + + // keep track of information about the current integration interval + epoch epoch_; + + time_type t_ = 0.; + time_type min_delay_; + std::vector<cell_group_ptr> cell_groups_; + + // one set of event_generators for each local cell + std::vector<std::vector<event_generator>> event_generators_; + + std::unique_ptr<spike_double_buffer> local_spikes_; + + // Hash table for looking up the the local index of a cell with a given gid + std::unordered_map<cell_gid_type, cell_size_type> gid_to_local_; + + util::optional<cell_size_type> local_cell_index(cell_gid_type); + + communicator communicator_; + + // Pending events to be delivered. + std::array<std::vector<pse_vector>, 2> event_lanes_; + std::vector<pse_vector> pending_events_; + + // Sampler associations handles are managed by a helper class. + util::handle_set<sampler_association_handle> sassoc_handles_; + + // Apply a functional to each cell group in parallel. + template <typename L> + void foreach_group(L fn) { + threading::parallel_for::apply(0, cell_groups_.size(), + [&](int i) { fn(cell_groups_[i]); }); + } + + // Apply a functional to each cell group in parallel, supplying + // the cell group pointer reference and index. + template <typename L> + void foreach_group_index(L fn) { + threading::parallel_for::apply(0, cell_groups_.size(), + [&](int i) { fn(cell_groups_[i], i); }); + } +}; + +simulation_state::simulation_state( + const recipe& rec, + const domain_decomposition& decomp, + const distributed_context* ctx + ): local_spikes_(new spike_double_buffer{}), communicator_(rec, decomp, ctx) { @@ -54,9 +137,8 @@ simulation::simulation(const recipe& rec, event_generators_.resize(num_local_cells); cell_local_size_type lidx = 0; - const auto& grps = decomp.groups; - for (auto i: util::make_span(0, grps.size())) { - for (auto gid: grps[i].gids) { + for (const auto& group_info: decomp.groups) { + for (auto gid: group_info.gids) { // Store mapping of gid to local cell index. gid_to_local_[gid] = lidx; @@ -79,10 +161,8 @@ simulation::simulation(const recipe& rec, // Generate the cell groups in parallel, with one task per cell group. cell_groups_.resize(decomp.groups.size()); - threading::parallel_for::apply(0, cell_groups_.size(), - [&](cell_gid_type i) { - cell_groups_[i] = cell_group_factory(rec, decomp.groups[i]); - }); + foreach_group_index( + [&](cell_group_ptr& group, int i) { group = cell_group_factory(rec, decomp.groups[i]); }); // Create event lane buffers. // There is one set for each epoch: current (0) and next (1). @@ -91,15 +171,12 @@ simulation::simulation(const recipe& rec, event_lanes_[1].resize(num_local_cells); } -simulation::~simulation() = default; - -void simulation::reset() { +void simulation_state::reset() { t_ = 0.; // Reset cell group state. - for (auto& group: cell_groups_) { - group->reset(); - } + foreach_group( + [](cell_group_ptr& group) { group->reset(); }); // Clear all pending events in the event lanes. for (auto& lanes: event_lanes_) { @@ -126,7 +203,7 @@ void simulation::reset() { local_spikes_->previous().clear(); } -time_type simulation::run(time_type tfinal, time_type dt) { +time_type simulation_state::run(time_type tfinal, time_type dt) { // Calculate the size of the largest possible time integration interval // before communication of spikes is required. // If spike exchange and cell update are serialized, this is the @@ -136,15 +213,11 @@ time_type simulation::run(time_type tfinal, time_type dt) { // task that updates cell state in parallel. auto update_cells = [&] () { - threading::parallel_for::apply( - 0u, cell_groups_.size(), - [&](unsigned i) { - auto &group = cell_groups_[i]; - - auto queues = util::subrange_view( - event_lanes(epoch_.id), - communicator_.group_queue_range(i)); + foreach_group_index( + [&](cell_group_ptr& group, int i) { + auto queues = util::subrange_view(event_lanes(epoch_.id), communicator_.group_queue_range(i)); group->advance(epoch_, dt, queues); + PE(advance_spikes); local_spikes_->current().insert(group->spikes()); group->clear_spikes(); @@ -163,8 +236,12 @@ time_type simulation::run(time_type tfinal, time_type dt) { auto global_spikes = communicator_.exchange(local_spikes); PE(communication_spikeio); - local_export_callback_(local_spikes); - global_export_callback_(global_spikes.values()); + if (local_export_callback_) { + local_export_callback_(local_spikes); + } + if (global_export_callback_) { + global_export_callback_(global_spikes.values()); + } PL(); PE(communication_walkspikes); @@ -214,7 +291,7 @@ time_type simulation::run(time_type tfinal, time_type dt) { // event_lanes[epoch]: take all events ≥ t_from // event_generators : take all events < t_to // pending_events : take all events -void simulation::setup_events(time_type t_from, time_type t_to, std::size_t epoch) { +void simulation_state::setup_events(time_type t_from, time_type t_to, std::size_t epoch) { const auto n = communicator_.num_local_cells(); threading::parallel_for::apply(0, n, [&](cell_size_type i) { @@ -228,7 +305,7 @@ void simulation::setup_events(time_type t_from, time_type t_to, std::size_t epoc }); } -sampler_association_handle simulation::add_sampler( +sampler_association_handle simulation_state::add_sampler( cell_member_predicate probe_ids, schedule sched, sampler_function f, @@ -236,83 +313,100 @@ sampler_association_handle simulation::add_sampler( { sampler_association_handle h = sassoc_handles_.acquire(); - threading::parallel_for::apply(0, cell_groups_.size(), - [&](std::size_t i) { - cell_groups_[i]->add_sampler(h, probe_ids, sched, f, policy); - }); + foreach_group( + [&](cell_group_ptr& group) { group->add_sampler(h, probe_ids, sched, f, policy); }); return h; } -void simulation::remove_sampler(sampler_association_handle h) { - threading::parallel_for::apply(0, cell_groups_.size(), - [&](std::size_t i) { - cell_groups_[i]->remove_sampler(h); - }); +void simulation_state::remove_sampler(sampler_association_handle h) { + foreach_group( + [h](cell_group_ptr& group) { group->remove_sampler(h); }); sassoc_handles_.release(h); } -void simulation::remove_all_samplers() { - threading::parallel_for::apply(0, cell_groups_.size(), - [&](std::size_t i) { - cell_groups_[i]->remove_all_samplers(); - }); +void simulation_state::remove_all_samplers() { + foreach_group( + [](cell_group_ptr& group) { group->remove_all_samplers(); }); sassoc_handles_.clear(); } -std::size_t simulation::num_spikes() const { - return communicator_.num_spikes(); +void simulation_state::set_binning_policy(binning_kind policy, time_type bin_interval) { + foreach_group( + [&](cell_group_ptr& group) { group->set_binning_policy(policy, bin_interval); }); +} + +void simulation_state::inject_events(const pse_vector& events) { + // Push all events that are to be delivered to local cells into the + // pending event list for the event's target cell. + for (auto& e: events) { + if (e.time<t_) { + throw bad_event_time(e.time, t_); + } + // gid_to_local_ maps gid to index into local set of cells. + if (auto lidx = util::value_by_key(gid_to_local_, e.target.gid)) { + pending_events_[*lidx].push_back(e); + } + } +} + +// Simulation class implementations forward to implementation class. + +simulation::simulation( + const recipe& rec, + const domain_decomposition& decomp, + const distributed_context* ctx) +{ + impl_.reset(new simulation_state(rec, decomp, ctx)); +} + +void simulation::reset() { + impl_->reset(); +} + +time_type simulation::run(time_type tfinal, time_type dt) { + return impl_->run(tfinal, dt); } -std::size_t simulation::num_groups() const { - return cell_groups_.size(); +sampler_association_handle simulation::add_sampler( + cell_member_predicate probe_ids, + schedule sched, + sampler_function f, + sampling_policy policy) +{ + return impl_->add_sampler(std::move(probe_ids), std::move(sched), std::move(f), policy); +} + +void simulation::remove_sampler(sampler_association_handle h) { + impl_->remove_sampler(h); } -std::vector<pse_vector>& simulation::event_lanes(std::size_t epoch_id) { - return event_lanes_[epoch_id%2]; +void simulation::remove_all_samplers() { + impl_->remove_all_samplers(); +} + +std::size_t simulation::num_spikes() const { + return impl_->num_spikes(); } void simulation::set_binning_policy(binning_kind policy, time_type bin_interval) { - for (auto& group: cell_groups_) { - group->set_binning_policy(policy, bin_interval); - } + impl_->set_binning_policy(policy, bin_interval); } void simulation::set_global_spike_callback(spike_export_function export_callback) { - global_export_callback_ = std::move(export_callback); + impl_->global_export_callback_ = std::move(export_callback); } void simulation::set_local_spike_callback(spike_export_function export_callback) { - local_export_callback_ = std::move(export_callback); -} - -util::optional<cell_size_type> simulation::local_cell_index(cell_gid_type gid) { - auto it = gid_to_local_.find(gid); - return it==gid_to_local_.end()? - util::nullopt: - util::optional<cell_size_type>(it->second); + impl_->local_export_callback_ = std::move(export_callback); } void simulation::inject_events(const pse_vector& events) { - // Push all events that are to be delivered to local cells into the - // pending event list for the event's target cell. - for (auto& e: events) { - if (e.time<t_) { - throw std::runtime_error( - "simulation::inject_events(): attempt to inject an event at time: " - + std::to_string(e.time) - + " ms, which is earlier than the current simulation time: " - + std::to_string(t_) - + " ms. Events must be injected on or after the current simulation time."); - } - // local_cell_index returns an optional type that evaluates - // to true iff the gid is a local cell. - if (auto lidx = local_cell_index(e.target.gid)) { - pending_events_[*lidx].push_back(e); - } - } + impl_->inject_events(events); } +simulation::~simulation() = default; + } // namespace arb diff --git a/arbor/simulation.hpp b/arbor/simulation.hpp deleted file mode 100644 index d5be946e8a0cf66cf109cd2f0696686bc09b5106..0000000000000000000000000000000000000000 --- a/arbor/simulation.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include <array> -#include <memory> -#include <unordered_map> -#include <vector> - -#include <arbor/common_types.hpp> -#include <arbor/distributed_context.hpp> -#include <arbor/sampling.hpp> - -#include "backends.hpp" -#include "cell_group.hpp" -#include "communication/communicator.hpp" -#include "domain_decomposition.hpp" -#include "epoch.hpp" -#include "recipe.hpp" -#include "util/nop.hpp" -#include "util/handle_set.hpp" - -namespace arb { - -class spike_double_buffer; - -class simulation { -public: - using spike_export_function = std::function<void(const std::vector<spike>&)>; - - simulation(const recipe& rec, const domain_decomposition& decomp, const distributed_context* ctx); - - void reset(); - - time_type run(time_type tfinal, time_type dt); - - // Note: sampler functions may be invoked from a different thread than that - // which called the `run` method. - - sampler_association_handle add_sampler(cell_member_predicate probe_ids, - schedule sched, sampler_function f, sampling_policy policy = sampling_policy::lax); - - void remove_sampler(sampler_association_handle); - - void remove_all_samplers(); - - std::size_t num_spikes() const; - - // Set event binning policy on all our groups. - void set_binning_policy(binning_kind policy, time_type bin_interval); - - // Register a callback that will perform a export of the global - // spike vector. - void set_global_spike_callback(spike_export_function export_callback); - - // Register a callback that will perform a export of the rank local - // spike vector. - void set_local_spike_callback(spike_export_function export_callback); - - // Add events directly to targets. - // Must be called before calling simulation::run, and must contain events that - // are to be delivered at or after the current simulation time. - void inject_events(const pse_vector& events); - - ~simulation(); - -private: - // Private helper function that sets up the event lanes for an epoch. - // See comments on implementation for more information. - void setup_events(time_type t_from, time_type time_to, std::size_t epoch_id); - - std::vector<pse_vector>& event_lanes(std::size_t epoch_id); - - std::size_t num_groups() const; - - // keep track of information about the current integration interval - epoch epoch_; - - time_type t_ = 0.; - time_type min_delay_; - std::vector<cell_group_ptr> cell_groups_; - - // one set of event_generators for each local cell - std::vector<std::vector<event_generator>> event_generators_; - - std::unique_ptr<spike_double_buffer> local_spikes_; - - spike_export_function global_export_callback_ = util::nop_function; - spike_export_function local_export_callback_ = util::nop_function; - - // Hash table for looking up the the local index of a cell with a given gid - std::unordered_map<cell_gid_type, cell_size_type> gid_to_local_; - - util::optional<cell_size_type> local_cell_index(cell_gid_type); - - communicator communicator_; - - // Pending events to be delivered. - std::array<std::vector<pse_vector>, 2> event_lanes_; - std::vector<pse_vector> pending_events_; - - // Sampler associations handles are managed by a helper class. - util::handle_set<sampler_association_handle> sassoc_handles_; -}; - -} // namespace arb diff --git a/arbor/spike_event_io.cpp b/arbor/spike_event_io.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f035ae860d9a5d759097825df299282f929f6a8d --- /dev/null +++ b/arbor/spike_event_io.cpp @@ -0,0 +1,11 @@ +#include <iostream> + +#include <arbor/spike_event.hpp> + +namespace arb { + +std::ostream& operator<<(std::ostream& o, const spike_event& ev) { + return o << "E[tgt " << ev.target << ", t " << ev.time << ", w " << ev.weight << "]"; +} + +} // namespace arb diff --git a/arbor/spike_source_cell_group.cpp b/arbor/spike_source_cell_group.cpp index d62ac2c2ca6c8fd60455082637c374d58a370e28..3859ba0c57a0903a93f6abd07d7b816c83400cba 100644 --- a/arbor/spike_source_cell_group.cpp +++ b/arbor/spike_source_cell_group.cpp @@ -1,11 +1,11 @@ #include <exception> +#include <arbor/recipe.hpp> #include <arbor/spike_source_cell.hpp> #include <arbor/time_sequence.hpp> #include "cell_group.hpp" #include "profile/profiler_macro.hpp" -#include "recipe.hpp" #include "spike_source_cell_group.hpp" #include "util/span.hpp" @@ -21,7 +21,7 @@ spike_source_cell_group::spike_source_cell_group(std::vector<cell_gid_type> gids time_sequences_.push_back(std::move(cell.seq)); } catch (util::bad_any_cast& e) { - throw std::runtime_error("model cell type mismatch: gid "+std::to_string(gid)+" is not a spike_source_cell"); + throw bad_cell_description(cell_kind::spike_source, gid); } } } diff --git a/arbor/spike_source_cell_group.hpp b/arbor/spike_source_cell_group.hpp index da714570add5d6e4ece3e7be335cc379116191c0..7169fb08bef2c887fbde6cbdf334c2ab49aa9205 100644 --- a/arbor/spike_source_cell_group.hpp +++ b/arbor/spike_source_cell_group.hpp @@ -1,9 +1,15 @@ #pragma once +#include <vector> + +#include <arbor/common_types.hpp> +#include <arbor/recipe.hpp> +#include <arbor/sampling.hpp> +#include <arbor/spike.hpp> #include <arbor/time_sequence.hpp> #include "cell_group.hpp" -#include "recipe.hpp" +#include "epoch.hpp" namespace arb { diff --git a/arbor/threading/threading.cpp b/arbor/threading/threading.cpp index 9f9855037a1eafe4a129ca2a5efe596fb521127d..6e2ab43edb4ac59c54bca4a39973255304e174d9 100644 --- a/arbor/threading/threading.cpp +++ b/arbor/threading/threading.cpp @@ -3,10 +3,12 @@ #include <regex> #include <string> +#include <arbor/arbexcept.hpp> #include <arbor/util/optional.hpp> #include <hardware/affinity.hpp> #include "threading.hpp" +#include "util/strprintf.hpp" namespace arb { namespace threading { @@ -49,8 +51,8 @@ util::optional<size_t> get_env_num_threads() { if (errno==ERANGE || !std::regex_match(str, std::regex("\\s*\\d*[0-9]\\d*\\s*"))) { - throw std::runtime_error("The requested number of threads \"" - +std::string(str)+"\" is not a valid value\n"); + throw arbor_exception(util::pprintf( + "requested number of threads \"{}\" is not a valid value", str)); } return nthreads; diff --git a/arbor/util/pprintf.hpp b/arbor/util/pprintf.hpp deleted file mode 100644 index f93d58f56905371def8b6d3bd3089344f5cfe564..0000000000000000000000000000000000000000 --- a/arbor/util/pprintf.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/* - * printf with variadic templates - */ - -#include <string> -#include <sstream> - -namespace arb { -namespace util { - -inline std::string pprintf(const char *s) { - std::string errstring; - while(*s) { - if(*s == '%' && s[1]!='%') { - // instead of throwing an exception, replace with ?? - //throw std::runtime_error("pprintf: the number of arguments did not match the format "); - errstring += "<?>"; - } - else { - errstring += *s; - } - ++s; - } - return errstring; -} - -template <typename T, typename ... Args> -std::string pprintf(const char *s, T value, Args... args) { - std::string errstring; - while(*s) { - if(*s == '%' && s[1]!='%') { - std::stringstream str; - str << value; - errstring += str.str(); - errstring += pprintf(++s, args...); - return errstring; - } - else { - errstring += *s; - ++s; - } - } - return errstring; -} - -} // namespace util -} // namespace arb diff --git a/arbor/util/strprintf.hpp b/arbor/util/strprintf.hpp index 738353f9631347decb19cc4baae7a5b511cc7f61..f548e79b1136bfdff828e42e790cf2da7af9ee48 100644 --- a/arbor/util/strprintf.hpp +++ b/arbor/util/strprintf.hpp @@ -1,18 +1,53 @@ #pragma once -/* Thin wrapper around std::snprintf for sprintf-like formatting - * to std::string. */ +// printf-like routines that return std::string. +// +// TODO: Consolidate with a single routine that provides a consistent interface +// along the lines of the PO645R2 text formatting proposal. #include <cstdio> #include <memory> #include <string> +#include <sstream> #include <system_error> #include <utility> #include <vector> +#include "util/meta.hpp" + namespace arb { namespace util { +// Use ADL to_string or std::to_string, falling back to ostream formatting: + +namespace impl_to_string { + using std::to_string; + + template <typename T, typename = void> + struct select { + static std::string str(const T& value) { + std::ostringstream o; + o << value; + return o.str(); + } + }; + + template <typename T> + struct select<T, util::void_t<decltype(to_string(std::declval<T>()))>> { + static std::string str(const T& v) { + return to_string(v); + } + }; +} + +template <typename T> +std::string to_string(const T& value) { + return impl_to_string::select<T>::str(value); +} + +// Use snprintf to format a string, with special handling for standard +// smart pointer types and strings. + namespace impl { template <typename X> X sprintf_arg_translate(const X& x) { return x; } @@ -47,5 +82,34 @@ std::string strprintf(const std::string& fmt, Args&&... args) { return strprintf(fmt.c_str(), std::forward<Args>(args)...); } +// Substitute instances of '{}' in the format string with the following parameters, +// using default std::ostream formatting. + +namespace impl { + inline void pprintf_(std::ostringstream& o, const char* s) { + o << s; + } + + template <typename T, typename... Tail> + void pprintf_(std::ostringstream& o, const char* s, T&& value, Tail&&... tail) { + const char* t = s; + while (*t && !(*t=='{' && t[1]=='}')) { + ++t; + } + o.write(s, t-s); + if (*t) { + o << std::forward<T>(value); + pprintf_(o, t+2, std::forward<Tail>(tail)...); + } + } +} + +template <typename... Args> +std::string pprintf(const char *s, Args&&... args) { + std::ostringstream o; + impl::pprintf_(o, s, std::forward<Args>(args)...); + return o.str(); +} + } // namespace util } // namespace arb diff --git a/example/bench/bench.cpp b/example/bench/bench.cpp index 4f5bff6ab6a6aa9f7fbe6a48a2adbdc097b54f0e..1ac4dcf19b6259a2265862451a539399939bb7d2 100644 --- a/example/bench/bench.cpp +++ b/example/bench/bench.cpp @@ -11,14 +11,17 @@ #include <arbor/profile/meter_manager.hpp> #include <arbor/common_types.hpp> #include <arbor/distributed_context.hpp> +#include <arbor/profile/profiler.hpp> +#include <arbor/recipe.hpp> +#include <arbor/simulation.hpp> -#include <hardware/node_info.hpp> -#include <load_balance.hpp> -#include <simulation.hpp> -#include <util/ioutil.hpp> +#include "hardware/node_info.hpp" +#include "load_balance.hpp" +#include "util/ioutil.hpp" #include "json_meter.hpp" +#include "parameters.hpp" #include "recipe.hpp" using namespace arb; diff --git a/example/bench/recipe.hpp b/example/bench/recipe.hpp index f06952be902c6f0f60930aa4a70825386f1c661b..4c8c8102f21f10d33a0861c691a73051e03d4e78 100644 --- a/example/bench/recipe.hpp +++ b/example/bench/recipe.hpp @@ -1,8 +1,7 @@ #pragma once #include <arbor/common_types.hpp> - -#include <recipe.hpp> +#include <arbor/recipe.hpp> #include "parameters.hpp" diff --git a/example/brunel/brunel_miniapp.cpp b/example/brunel/brunel_miniapp.cpp index bb2af814751972a59c5f81b0091b648ba4ddfa9f..ef07bf2c6198b99df4148cbd7cb89b42729dda4c 100644 --- a/example/brunel/brunel_miniapp.cpp +++ b/example/brunel/brunel_miniapp.cpp @@ -8,9 +8,12 @@ #include <arbor/common_types.hpp> #include <arbor/distributed_context.hpp> +#include <arbor/event_generator.hpp> #include <arbor/lif_cell.hpp> #include <arbor/profile/meter_manager.hpp> #include <arbor/profile/profiler.hpp> +#include <arbor/recipe.hpp> +#include <arbor/simulation.hpp> #include <arbor/threadinfo.hpp> #include <arbor/util/make_unique.hpp> #include <arbor/version.hpp> @@ -20,12 +23,9 @@ #include "with_mpi.hpp" #endif -#include "event_generator.hpp" #include "hardware/gpu.hpp" #include "hardware/node_info.hpp" #include "io/exporter_spike_file.hpp" -#include "recipe.hpp" -#include "simulation.hpp" #include "util/ioutil.hpp" #include "partitioner.hpp" diff --git a/example/brunel/partitioner.hpp b/example/brunel/partitioner.hpp index 1aaae53ae1665af17092a1759f64c775ec204cd4..cd4d383d37eef21743e8d9ddc532a4cec5d7a825 100644 --- a/example/brunel/partitioner.hpp +++ b/example/brunel/partitioner.hpp @@ -1,8 +1,11 @@ #include <arbor/distributed_context.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> -#include "domain_decomposition.hpp" #include "hardware/node_info.hpp" -#include "recipe.hpp" +#include "util/partition.hpp" +#include "util/span.hpp" +#include "util/transform.hpp" namespace arb { static @@ -33,7 +36,7 @@ namespace arb { // Global load balance std::vector<cell_gid_type> gid_divisions; auto gid_part = make_partition( - gid_divisions, util::transform_view(util::make_span(0, num_domains), dom_size)); + gid_divisions, util::transform_view(util::make_span(num_domains), dom_size)); auto range = gid_part[domain_id]; cell_size_type num_local_cells = range.second - range.first; diff --git a/example/generators/event_gen.cpp b/example/generators/event_gen.cpp index 5a21764d7edb4e7bbd6b38ace3720096601f8c39..22607137bf164242b1996c4cc2c7b5981ca352b0 100644 --- a/example/generators/event_gen.cpp +++ b/example/generators/event_gen.cpp @@ -14,14 +14,14 @@ #include <arbor/common_types.hpp> #include <arbor/distributed_context.hpp> +#include <arbor/event_generator.hpp> #include <arbor/mc_cell.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/recipe.hpp> +#include <arbor/simulation.hpp> -#include "event_generator.hpp" #include "hardware/node_info.hpp" #include "load_balance.hpp" -#include "simulation.hpp" -#include "recipe.hpp" using arb::cell_gid_type; using arb::cell_lid_type; diff --git a/example/miniapp/miniapp.cpp b/example/miniapp/miniapp.cpp index 6f43f54a4f5b4ab0175a56592b5229158cc45b50..2fb8be9cadfd8d6eaf4ac58f6502205877252059 100644 --- a/example/miniapp/miniapp.cpp +++ b/example/miniapp/miniapp.cpp @@ -11,17 +11,16 @@ #include <arbor/profile/meter_manager.hpp> #include <arbor/profile/profiler.hpp> #include <arbor/sampling.hpp> +#include <arbor/schedule.hpp> +#include <arbor/simulation.hpp> #include <arbor/threadinfo.hpp> #include <arbor/util/any.hpp> #include <arbor/version.hpp> -#include "communication/communicator.hpp" #include "hardware/gpu.hpp" #include "hardware/node_info.hpp" #include "io/exporter_spike_file.hpp" #include "load_balance.hpp" -#include "simulation.hpp" -#include "schedule.hpp" #include "util/ioutil.hpp" #include "json_meter.hpp" @@ -36,7 +35,6 @@ using namespace arb; using util::any_cast; -using util::make_span; void banner(hw::node_info, const distributed_context*); std::unique_ptr<recipe> make_recipe(const io::cl_options&, const probe_distribution&); @@ -102,7 +100,7 @@ int main(int argc, char** argv) { continue; } - for (cell_lid_type j: make_span(0, recipe->num_probes(gid))) { + for (cell_lid_type j = 0; j<recipe->num_probes(gid); ++j) { sample_traces.push_back(make_trace(recipe->get_probe({gid, j}))); } } diff --git a/example/miniapp/miniapp_recipes.cpp b/example/miniapp/miniapp_recipes.cpp index dc250abc0e3a0583d58c9d77b179a0aa90683c5a..50838d1a5d0ef0e7511eba2eb60eeca1ccbe89e0 100644 --- a/example/miniapp/miniapp_recipes.cpp +++ b/example/miniapp/miniapp_recipes.cpp @@ -4,12 +4,12 @@ #include <utility> #include <arbor/assert.hpp> +#include <arbor/event_generator.hpp> #include <arbor/mc_cell.hpp> #include <arbor/morphology.hpp> #include <arbor/spike_source_cell.hpp> #include <arbor/time_sequence.hpp> -#include "event_generator.hpp" #include "io.hpp" #include "miniapp_recipes.hpp" @@ -109,7 +109,7 @@ public: probe_info get_probe(cell_member_type probe_id) const override { if (probe_id.index>=num_probes(probe_id.gid)) { - throw invalid_recipe_error("invalid probe id"); + throw arb::bad_probe_id(probe_id); } // if we have both voltage and current probes, then order them @@ -305,7 +305,7 @@ public: basic_cell_recipe(ncell, std::move(param), std::move(pdist)) { if (std::size_t(param.num_synapses) != ncell-1) { - throw invalid_recipe_error("number of synapses per cell must equal number " + throw std::runtime_error("number of synapses per cell must equal number " "of cells minus one in complete graph model"); } } diff --git a/example/miniapp/miniapp_recipes.hpp b/example/miniapp/miniapp_recipes.hpp index 684b58b8b24c475a55385ace9350fbfd057e9eea..94103c1fac4775d873326537542f0cec71b61804 100644 --- a/example/miniapp/miniapp_recipes.hpp +++ b/example/miniapp/miniapp_recipes.hpp @@ -5,8 +5,7 @@ #include <stdexcept> #include <arbor/util/optional.hpp> - -#include "recipe.hpp" +#include <arbor/recipe.hpp> #include "morphology_pool.hpp" diff --git a/include/arbor/arbexcept.hpp b/include/arbor/arbexcept.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d136362a0ecec42a6e4ce6176f06d80ed80bfdbd --- /dev/null +++ b/include/arbor/arbexcept.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include <stdexcept> +#include <string> + +#include <arbor/common_types.hpp> + +// Arbor-specific exception hierarchy. + +namespace arb { + +// Arbor internal logic error (if these are thrown, +// there is a bug in the library.) + +struct arbor_internal_error: std::logic_error { + arbor_internal_error(const std::string& what_arg): + std::logic_error(what_arg) + {} +}; + + +// Common base-class for arbor run-time errors. + +struct arbor_exception: std::runtime_error { + arbor_exception(const std::string& what_arg): + std::runtime_error(what_arg) + {} +}; + +// Recipe errors: + +struct bad_cell_description: arbor_exception { + bad_cell_description(cell_kind kind, cell_gid_type gid); + cell_gid_type gid; + cell_kind kind; +}; + +struct bad_global_property: arbor_exception { + explicit bad_global_property(cell_kind kind); + cell_kind kind; +}; + +struct bad_probe_id: arbor_exception { + explicit bad_probe_id(cell_member_type id); + cell_member_type probe_id; +}; + +// Simulation errors: + +struct bad_event_time: arbor_exception { + explicit bad_event_time(time_type event_time, time_type sim_time); + time_type event_time; + time_type sim_time; +}; + +// Mechanism catalogue errors: + +struct no_such_mechanism: arbor_exception { + explicit no_such_mechanism(const std::string& mech_name); + std::string mech_name; +}; + +struct duplicate_mechanism: arbor_exception { + explicit duplicate_mechanism(const std::string& mech_name); + std::string mech_name; +}; + +struct fingerprint_mismatch: arbor_exception { + explicit fingerprint_mismatch(const std::string& mech_name); + std::string mech_name; +}; + +struct no_such_parameter: arbor_exception { + no_such_parameter(const std::string& mech_name, const std::string& param_name); + std::string mech_name; + std::string param_name; +}; + +struct invalid_parameter_value: arbor_exception { + invalid_parameter_value(const std::string& mech_name, const std::string& param_name, double value); + std::string mech_name; + std::string param_name; + double value; +}; + +struct no_such_implementation: arbor_exception { + explicit no_such_implementation(const std::string& mech_name); + std::string mech_name; +}; + +// Run-time value bounds check: + +struct range_check_failure: arbor_exception { + explicit range_check_failure(const std::string& whatstr, double value); + double value; +}; + +} // namespace arb diff --git a/include/arbor/common_types.hpp b/include/arbor/common_types.hpp index aed91121afc1117609dc029d10cab95e228d8854..923cf6b62c607e23509c775318f9855be89a2417 100644 --- a/include/arbor/common_types.hpp +++ b/include/arbor/common_types.hpp @@ -64,6 +64,13 @@ using probe_tag = int; using sample_size_type = std::int32_t; +// Enumeration for execution back-end targets, as specified in domain decompositions. + +enum class backend_kind { + multicore, // Use multicore back-end for all computation. + gpu // Use gpu back-end when supported by cell_group implementation. +}; + // Enumeration used to indentify the cell type/kind, used by the model to // group equal kinds in the same cell group. @@ -74,10 +81,19 @@ enum class cell_kind { benchmark, // Proxy cell used for benchmarking. }; -} // namespace arb +// Enumeration for event time binning policy. -std::ostream& operator<<(std::ostream& O, arb::cell_member_type m); -std::ostream& operator<<(std::ostream& O, arb::cell_kind k); +enum class binning_kind { + none, + regular, // => round time down to multiple of binning interval. + following, // => round times down to previous event if within binning interval. +}; + +std::ostream& operator<<(std::ostream& o, cell_member_type m); +std::ostream& operator<<(std::ostream& o, cell_kind k); +std::ostream& operator<<(std::ostream& o, backend_kind k); + +} // namespace arb namespace std { template <> struct hash<arb::cell_member_type> { diff --git a/arbor/domain_decomposition.hpp b/include/arbor/domain_decomposition.hpp similarity index 85% rename from arbor/domain_decomposition.hpp rename to include/arbor/domain_decomposition.hpp index b225970d7e3d08204592ab9163a7b2298ebf8e25..aecf5a83d287200718d04695328a71358c22604a 100644 --- a/arbor/domain_decomposition.hpp +++ b/include/arbor/domain_decomposition.hpp @@ -1,19 +1,11 @@ #pragma once +#include <algorithm> #include <functional> -#include <type_traits> -#include <unordered_map> #include <vector> +#include <arbor/assert.hpp> #include <arbor/common_types.hpp> -#include <arbor/util/optional.hpp> - -#include "backends.hpp" -#include "hardware/node_info.hpp" -#include "recipe.hpp" -#include "util/partition.hpp" -#include "util/range.hpp" -#include "util/transform.hpp" namespace arb { @@ -38,7 +30,7 @@ struct group_description { group_description(cell_kind k, std::vector<cell_gid_type> g, backend_kind b): kind(k), gids(std::move(g)), backend(b) { - arb_assert(util::is_sorted(gids)); + arb_assert(std::is_sorted(gids.begin(), gids.end())); } }; diff --git a/arbor/event_generator.hpp b/include/arbor/event_generator.hpp similarity index 84% rename from arbor/event_generator.hpp rename to include/arbor/event_generator.hpp index 048b62ebaf52569f8fb67c3c7fc2a230ed4eed34..e18290d7d28de9c95c1c7f74d817b579f0166122 100644 --- a/arbor/event_generator.hpp +++ b/include/arbor/event_generator.hpp @@ -5,24 +5,22 @@ #include <random> #include <arbor/common_types.hpp> +#include <arbor/generic_event.hpp> +#include <arbor/spike_event.hpp> #include <arbor/time_sequence.hpp> -#include "event_queue.hpp" -#include "util/range.hpp" -#include "util/rangeutil.hpp" - namespace arb { // Generate a postsynaptic spike event that has delivery time set to // terminal_time. Such events are used as sentinels, to indicate the // end of a sequence. inline constexpr -postsynaptic_spike_event make_terminal_pse() { - return postsynaptic_spike_event{cell_member_type{0,0}, terminal_time, 0}; +spike_event make_terminal_pse() { + return spike_event{cell_member_type{0,0}, terminal_time, 0}; } inline -bool is_terminal_pse(const postsynaptic_spike_event& e) { +bool is_terminal_pse(const spike_event& e) { return e.time==terminal_time; } @@ -31,8 +29,8 @@ bool is_terminal_pse(const postsynaptic_spike_event& e) { // Declared ahead of event_generator so that it can be used as the default // generator. struct empty_generator { - postsynaptic_spike_event front() { - return postsynaptic_spike_event{cell_member_type{0,0}, terminal_time, 0}; + spike_event front() { + return spike_event{cell_member_type{0,0}, terminal_time, 0}; } void pop() {} void reset() {} @@ -78,7 +76,7 @@ public: // Does not modify the state of the stream, i.e. multiple calls to // front() will return the same event in the absence of calls to pop(), // advance() or reset(). - postsynaptic_spike_event front() { + spike_event front() { return impl_->front(); } @@ -100,7 +98,7 @@ public: private: struct interface { - virtual postsynaptic_spike_event front() = 0; + virtual spike_event front() = 0; virtual void pop() = 0; virtual void advance(time_type t) = 0; virtual void reset() = 0; @@ -115,7 +113,7 @@ private: explicit wrap(const Impl& impl): wrapped(impl) {} explicit wrap(Impl&& impl): wrapped(std::move(impl)) {} - postsynaptic_spike_event front() override { + spike_event front() override { return wrapped.front(); } @@ -142,15 +140,15 @@ private: // Generator that feeds events that are specified with a vector. // Makes a copy of the input sequence of events. struct vector_backed_generator { - using pse = postsynaptic_spike_event; + using pse = spike_event; vector_backed_generator(cell_member_type target, float weight, std::vector<time_type> samples): target_(target), weight_(weight), tseq_(std::move(samples)) {} - postsynaptic_spike_event front() { - return postsynaptic_spike_event{target_, tseq_.front(), weight_}; + spike_event front() { + return spike_event{target_, tseq_.front(), weight_}; } void pop() { @@ -177,15 +175,15 @@ private: // does not outlive the sequence. template <typename Seq> struct seq_generator { - using pse = postsynaptic_spike_event; + using pse = spike_event; seq_generator(Seq& events): events_(events), it_(std::begin(events_)) { - arb_assert(util::is_sorted(events_)); + arb_assert(std::is_sorted(events_.begin(), events_.end())); } - postsynaptic_spike_event front() { + spike_event front() { return it_==events_.end()? make_terminal_pse(): *it_; } @@ -212,7 +210,7 @@ private: // * with delivery times t=t_start+n*dt, ∀ t ∈ [t_start, t_stop) // * with a set target and weight struct regular_generator { - using pse = postsynaptic_spike_event; + using pse = spike_event; regular_generator(cell_member_type target, float weight, @@ -224,8 +222,8 @@ struct regular_generator { tseq_(tstart, dt, tstop) {} - postsynaptic_spike_event front() { - return postsynaptic_spike_event{target_, tseq_.front(), weight_}; + spike_event front() { + return spike_event{target_, tseq_.front(), weight_}; } void pop() { @@ -250,7 +248,7 @@ private: // with rate_per_ms spikes per ms. template <typename RandomNumberEngine> struct poisson_generator { - using pse = postsynaptic_spike_event; + using pse = spike_event; poisson_generator(cell_member_type target, float weight, @@ -265,8 +263,8 @@ struct poisson_generator { reset(); } - postsynaptic_spike_event front() { - return postsynaptic_spike_event{target_, tseq_.front(), weight_}; + spike_event front() { + return spike_event{target_, tseq_.front(), weight_}; } void pop() { diff --git a/arbor/generic_event.hpp b/include/arbor/generic_event.hpp similarity index 100% rename from arbor/generic_event.hpp rename to include/arbor/generic_event.hpp diff --git a/include/arbor/mc_cell.hpp b/include/arbor/mc_cell.hpp index b9e4b101d51ce00735f8734d8ef4c0f3eabf83f3..71e6579428ff0dd8c091f252bd81590e72cc18a8 100644 --- a/include/arbor/mc_cell.hpp +++ b/include/arbor/mc_cell.hpp @@ -1,9 +1,10 @@ #pragma once #include <unordered_map> -#include <stdexcept> +#include <string> #include <vector> +#include <arbor/arbexcept.hpp> #include <arbor/common_types.hpp> #include <arbor/constants.hpp> #include <arbor/ion.hpp> @@ -11,9 +12,16 @@ #include <arbor/morphology.hpp> #include <arbor/mc_segment.hpp> - namespace arb { +// Specialize arbor exception for errors in cell building. + +struct mc_cell_error: arbor_exception { + mc_cell_error(const std::string& what): + arbor_exception("mc_cell: "+what) + {} +}; + // Location specification for point processes. struct segment_location { @@ -161,7 +169,7 @@ public: const soma_segment* soma() const; /// access pointer to a cable segment - /// will throw an std::out_of_range exception if + /// will throw an mc_cell_error exception if /// the cable index is not valid cable_segment* cable(index_type index); @@ -252,10 +260,8 @@ template <typename... Args> cable_segment* mc_cell::add_cable(mc_cell::index_type parent, Args&&... args) { // check for a valid parent id - if(parent>=num_segments()) { - throw std::out_of_range( - "parent index of cell segment is out of range" - ); + if (parent>=num_segments()) { + throw mc_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); diff --git a/arbor/recipe.hpp b/include/arbor/recipe.hpp similarity index 88% rename from arbor/recipe.hpp rename to include/arbor/recipe.hpp index 8d163c35bc558a6ac0ad235fb61f596a19b7f7e4..00147ff854cbecd8e824d91d4f13cd907abd992d 100644 --- a/arbor/recipe.hpp +++ b/include/arbor/recipe.hpp @@ -5,11 +5,11 @@ #include <unordered_map> #include <stdexcept> +#include <arbor/arbexcept.hpp> #include <arbor/common_types.hpp> +#include <arbor/event_generator.hpp> #include <arbor/util/unique_any.hpp> -#include "event_generator.hpp" - namespace arb { struct probe_info { @@ -20,11 +20,6 @@ struct probe_info { util::any address; }; -class invalid_recipe_error: public std::runtime_error { -public: - invalid_recipe_error(std::string whatstr): std::runtime_error(std::move(whatstr)) {} -}; - /* Recipe descriptions are cell-oriented: in order that the building * phase can be done distributedly and in order that the recipe * description can be built indepdently of any runtime execution environment. @@ -70,8 +65,9 @@ public: virtual std::vector<cell_connection> connections_on(cell_gid_type) const { return {}; } - virtual probe_info get_probe(cell_member_type) const { - throw std::logic_error("no probes"); + + virtual probe_info get_probe(cell_member_type probe_id) const { + throw bad_probe_id(probe_id); } // Global property type will be specific to given cell kind. diff --git a/arbor/schedule.hpp b/include/arbor/schedule.hpp similarity index 92% rename from arbor/schedule.hpp rename to include/arbor/schedule.hpp index 66086a841b1090fd97a1853354a2c601d1b9d7ed..15ddadaef5ee3ceb86ff0fb40c23ee0e5645d2f1 100644 --- a/arbor/schedule.hpp +++ b/include/arbor/schedule.hpp @@ -1,6 +1,7 @@ #pragma once #include <algorithm> +#include <iterator> #include <memory> #include <random> #include <vector> @@ -9,8 +10,6 @@ #include <arbor/common_types.hpp> #include <arbor/util/compat.hpp> -#include "util/meta.hpp" - // Time schedules for probe–sampler associations. namespace arb { @@ -102,11 +101,17 @@ inline schedule regular_schedule(time_type dt) { // Schedule at times given explicitly via a provided sorted sequence. class explicit_schedule_impl { public: - template <typename Seq, typename = util::enable_if_sequence_t<const Seq&>> + explicit_schedule_impl(const explicit_schedule_impl&) = default; + explicit_schedule_impl(explicit_schedule_impl&&) = default; + + template <typename Seq> explicit explicit_schedule_impl(const Seq& seq): - start_index_(0), - times_(std::begin(seq), compat::end(seq)) + start_index_(0) { + using std::begin; + using compat::end; // TODO: replace with std::end when we nuke xlC support. + + times_.assign(begin(seq), end(seq)); arb_assert(std::is_sorted(times_.begin(), times_.end())); } diff --git a/include/arbor/simulation.hpp b/include/arbor/simulation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c7844e9313f1516dbe222dc6ab3003d1e866f572 --- /dev/null +++ b/include/arbor/simulation.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include <array> +#include <memory> +#include <unordered_map> +#include <vector> + +#include <arbor/common_types.hpp> +#include <arbor/distributed_context.hpp> +#include <arbor/domain_decomposition.hpp> +#include <arbor/recipe.hpp> +#include <arbor/sampling.hpp> +#include <arbor/schedule.hpp> +#include <arbor/util/handle_set.hpp> + +namespace arb { + +using spike_export_function = std::function<void(const std::vector<spike>&)>; + +struct simulation_state; +class simulation { +public: + simulation(const recipe& rec, const domain_decomposition& decomp, const distributed_context* ctx); + + void reset(); + + time_type run(time_type tfinal, time_type dt); + + // Note: sampler functions may be invoked from a different thread than that + // which called the `run` method. + + sampler_association_handle add_sampler(cell_member_predicate probe_ids, + schedule sched, sampler_function f, sampling_policy policy = sampling_policy::lax); + + void remove_sampler(sampler_association_handle); + + void remove_all_samplers(); + + std::size_t num_spikes() const; + + // Set event binning policy on all our groups. + void set_binning_policy(binning_kind policy, time_type bin_interval); + + // Register a callback that will perform a export of the global + // spike vector. + void set_global_spike_callback(spike_export_function = spike_export_function{}); + + // Register a callback that will perform a export of the rank local + // spike vector. + void set_local_spike_callback(spike_export_function = spike_export_function{}); + + // Add events directly to targets. + // Must be called before calling simulation::run, and must contain events that + // are to be delivered at or after the current simulation time. + void inject_events(const pse_vector& events); + + ~simulation(); + +private: + std::unique_ptr<simulation_state> impl_; +}; + +} // namespace arb diff --git a/include/arbor/spike_event.hpp b/include/arbor/spike_event.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2b72314c3ca3fce11f8dc61c09391f15fc8962e1 --- /dev/null +++ b/include/arbor/spike_event.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <iosfwd> +#include <tuple> +#include <vector> + +#include <arbor/common_types.hpp> + +namespace arb { + +// Events delivered to targets on cells with a cell group. + +struct spike_event { + cell_member_type target; + time_type time; + float weight; + + friend bool operator==(const spike_event& l, const spike_event& r) { + return l.target==r.target && l.time==r.time && l.weight==r.weight; + } + + friend bool operator<(const spike_event& l, const spike_event& r) { + return std::tie(l.time, l.target, l.weight) < std::tie(r.time, r.target, r.weight); + } +}; + +using pse_vector = std::vector<spike_event>; + +std::ostream& operator<<(std::ostream&, const spike_event&); + +} // namespace arb diff --git a/include/arbor/util/compat.hpp b/include/arbor/util/compat.hpp index 7784f04724296a54a62cb57d01d615c86655becb..0603e02c391baba0ada1aa3d7fb73e5168177816 100644 --- a/include/arbor/util/compat.hpp +++ b/include/arbor/util/compat.hpp @@ -25,7 +25,7 @@ constexpr bool using_gnu_compiler(int major=0, int minor=0, int patchlevel=0) { #endif } -// std::end() broken with (at least) xlC 13.1.4. +// std::end() broken with xlC 13.1.4; fixed in 13.1.5. namespace impl { using std::end; @@ -43,6 +43,7 @@ template <typename T, std::size_t N> const T* end(const T (&x)[N]) { return &x[0]+N; } // Work-around bad optimization reordering in xlC 13.1.4. +// Note: still broken in xlC 14.1.0 inline void compiler_barrier_if_xlc_leq(unsigned ver) { #if defined(__xlC__) @@ -64,6 +65,7 @@ inline void compiler_barrier_if_icc_leq(unsigned ver) { // Work-around bad ordering of std::isinf() (sometimes) within switch, xlC 13.1.4; // wrapping the call within another function appears to be sufficient. +// Note: still broken in xlC 14.1.0. template <typename X> inline constexpr bool isinf(X x) { return std::isinf(x); } diff --git a/arbor/util/handle_set.hpp b/include/arbor/util/handle_set.hpp similarity index 100% rename from arbor/util/handle_set.hpp rename to include/arbor/util/handle_set.hpp diff --git a/include/arbor/util/uninitialized.hpp b/include/arbor/util/uninitialized.hpp index a94b3c62ec487764fc56686146b97f339e54f736..5c8e46e21354f8f80f7bd4999067f071b1ccbcd1 100644 --- a/include/arbor/util/uninitialized.hpp +++ b/include/arbor/util/uninitialized.hpp @@ -43,15 +43,15 @@ public: using const_rvalue_reference= const X&&; pointer ptr() { - // COMPAT: xlC 13.1.4 workaround: + // COMPAT: xlC 13.1.4 workaround (still broken in 14.1.0): // should be equivalent to `return reinterpret_cast<X*>(&data)`. - compat::compiler_barrier_if_xlc_leq(0x0d01); + compat::compiler_barrier_if_xlc_leq(0x0e01); return static_cast<X*>(static_cast<void*>(&data)); } const_pointer cptr() const { - // COMPAT: xlC 13.1.4 workaround: + // COMPAT: xlC 13.1.4 workaround (still broken in 14.1.0): // should be equivalent to `return reinterpret_cast<const X*>(&data)` - compat::compiler_barrier_if_xlc_leq(0x0d01); + compat::compiler_barrier_if_xlc_leq(0x0e01); return static_cast<const X*>(static_cast<const void*>(&data)); } diff --git a/test/common_cells.hpp b/test/common_cells.hpp index 3ced9dcbda3b89dac6dace5a2034770c53b73a4d..134395caf17a2c391f40b83c20688acea49ecfd0 100644 --- a/test/common_cells.hpp +++ b/test/common_cells.hpp @@ -3,8 +3,7 @@ #include <arbor/mc_cell.hpp> #include <arbor/mc_segment.hpp> #include <arbor/mechinfo.hpp> - -#include "recipe.hpp" +#include <arbor/recipe.hpp> namespace arb { diff --git a/test/simple_recipes.hpp b/test/simple_recipes.hpp index 531c66d891178fcc1064308b1ac1bd87f06866c2..92c392082191631e6ce7e98a4ad02c1efa4b288e 100644 --- a/test/simple_recipes.hpp +++ b/test/simple_recipes.hpp @@ -5,10 +5,9 @@ #include <unordered_map> #include <vector> +#include <arbor/event_generator.hpp> #include <arbor/mc_cell.hpp> - -#include <event_generator.hpp> -#include <recipe.hpp> +#include <arbor/recipe.hpp> namespace arb { diff --git a/test/ubench/event_binning.cpp b/test/ubench/event_binning.cpp index 94b58eba1cb16a9e910b784885eaab2e0e96d336..1c33cfa737a08d0a4814c9e772a2ebbde68eaa90 100644 --- a/test/ubench/event_binning.cpp +++ b/test/ubench/event_binning.cpp @@ -7,15 +7,15 @@ #include <unordered_map> #include <vector> -#include <event_queue.hpp> -#include <backends/event.hpp> +#include <arbor/spike_event.hpp> + +#include "event_queue.hpp" +#include "backends/event.hpp" #include <benchmark/benchmark.h> using namespace arb; -using pse = postsynaptic_spike_event; - std::vector<cell_gid_type> generate_gids(size_t n) { std::mt19937 engine; std::uniform_int_distribution<cell_gid_type> dist(1u, 3u); @@ -31,9 +31,9 @@ std::vector<cell_gid_type> generate_gids(size_t n) { return gids; } -std::vector<std::vector<pse>> generate_inputs(const std::vector<cell_gid_type>& gids, size_t ev_per_cell) { +std::vector<pse_vector> generate_inputs(const std::vector<cell_gid_type>& gids, size_t ev_per_cell) { auto ncells = gids.size(); - std::vector<std::vector<pse>> input_events; + std::vector<pse_vector> input_events; std::uniform_int_distribution<cell_gid_type>(0u, ncells); std::mt19937 gen; @@ -42,7 +42,7 @@ std::vector<std::vector<pse>> generate_inputs(const std::vector<cell_gid_type>& input_events.resize(ncells); for (std::size_t i=0; i<ncells*ev_per_cell; ++i) { - postsynaptic_spike_event ev; + spike_event ev; auto idx = gid_dist(gen); auto gid = gids[idx]; auto t = 1.; diff --git a/test/ubench/event_setup.cpp b/test/ubench/event_setup.cpp index 8d5cf3a134860780ee594052f50728d3bd5d0bc3..9931e8bac0a220c834448fff74ea65218a37b452 100644 --- a/test/ubench/event_setup.cpp +++ b/test/ubench/event_setup.cpp @@ -7,7 +7,7 @@ // gid. A similar lookup should be added to theses tests, to more accurately // reflect the mc_cell_group implementation. // -// TODO: The staged_events output is a vector of postsynaptic_spike_event, not +// TODO: The staged_events output is a vector of spike_event, not // a deliverable event. #include <random> @@ -20,8 +20,8 @@ using namespace arb; -std::vector<postsynaptic_spike_event> generate_inputs(size_t ncells, size_t ev_per_cell) { - std::vector<postsynaptic_spike_event> input_events; +std::vector<spike_event> generate_inputs(size_t ncells, size_t ev_per_cell) { + std::vector<spike_event> input_events; std::default_random_engine engine; std::uniform_int_distribution<cell_gid_type>(0u, ncells); @@ -33,7 +33,7 @@ std::vector<postsynaptic_spike_event> generate_inputs(size_t ncells, size_t ev_p input_events.reserve(ncells*ev_per_cell); for (std::size_t i=0; i<ncells*ev_per_cell; ++i) { - postsynaptic_spike_event ev; + spike_event ev; auto gid = gid_dist(gen); auto t = time_dist(gen); ev.target = {cell_gid_type(gid), cell_lid_type(0)}; @@ -46,7 +46,7 @@ std::vector<postsynaptic_spike_event> generate_inputs(size_t ncells, size_t ev_p } void single_queue(benchmark::State& state) { - using pev = postsynaptic_spike_event; + using pev = spike_event; const std::size_t ncells = state.range(0); const std::size_t ev_per_cell = state.range(1); @@ -84,7 +84,7 @@ void single_queue(benchmark::State& state) { } void n_queue(benchmark::State& state) { - using pev = postsynaptic_spike_event; + using pev = spike_event; const std::size_t ncells = state.range(0); const std::size_t ev_per_cell = state.range(1); @@ -123,7 +123,7 @@ void n_queue(benchmark::State& state) { } void n_vector(benchmark::State& state) { - using pev = postsynaptic_spike_event; + using pev = spike_event; const std::size_t ncells = state.range(0); const std::size_t ev_per_cell = state.range(1); @@ -165,7 +165,7 @@ void n_vector(benchmark::State& state) { part[i+1] = part[i] + ext[i]; } // copy events into the output flat buffer - std::vector<postsynaptic_spike_event> staged_events(part.back()); + std::vector<spike_event> staged_events(part.back()); auto b = staged_events.begin(); for (size_t i=0; i<ncells; ++i) { auto bi = event_lanes[i].begin(); diff --git a/test/unit-distributed/test_communicator.cpp b/test/unit-distributed/test_communicator.cpp index 151df0f31c2b499aa106f7a81e1262bca092aae2..3c3daf176c6de3a1aacfe0bf2a902d79152f0a22 100644 --- a/test/unit-distributed/test_communicator.cpp +++ b/test/unit-distributed/test_communicator.cpp @@ -5,13 +5,14 @@ #include <vector> #include <arbor/distributed_context.hpp> +#include <arbor/spike_event.hpp> -#include <communication/communicator.hpp> -#include <hardware/node_info.hpp> -#include <load_balance.hpp> -#include <util/filter.hpp> -#include <util/rangeutil.hpp> -#include <util/span.hpp> +#include "communication/communicator.hpp" +#include "hardware/node_info.hpp" +#include "load_balance.hpp" +#include "util/filter.hpp" +#include "util/rangeutil.hpp" +#include "util/span.hpp" using namespace arb; @@ -211,7 +212,7 @@ namespace { // gid expects an event from source_of(gid) with weight gid, and fired at // time source_of(gid). - postsynaptic_spike_event expected_event_ring(cell_gid_type gid, cell_size_type num_cells) { + spike_event expected_event_ring(cell_gid_type gid, cell_size_type num_cells) { auto sid = source_of(gid, num_cells); return { {gid, 0u}, // source @@ -267,7 +268,7 @@ namespace { cell_size_type ranks_; }; - postsynaptic_spike_event expected_event_all2all(cell_gid_type gid, cell_gid_type sid) { + spike_event expected_event_all2all(cell_gid_type gid, cell_gid_type sid) { return { {gid, sid}, // target, event from sid goes to synapse with index sid sid+1.0f, // time (all conns have delay 1 ms) diff --git a/test/unit/test_backend.cpp b/test/unit/test_backend.cpp index 5e1c18d4741d1efaa3f24d4e46e02a0f61cdb05e..74db35c23896190e96edfaf0654084fbfaf5c23e 100644 --- a/test/unit/test_backend.cpp +++ b/test/unit/test_backend.cpp @@ -1,6 +1,6 @@ +#include <arbor/common_types.hpp> #include <arbor/version.hpp> -#include "backends.hpp" #include "fvm_lowered_cell.hpp" #include "../gtest.h" diff --git a/test/unit/test_domain_decomposition.cpp b/test/unit/test_domain_decomposition.cpp index 16dd8785af68acd8047dabe472a03e7ed8ec6b68..dc27a4337cff92f565b995890506e4a8495954f3 100644 --- a/test/unit/test_domain_decomposition.cpp +++ b/test/unit/test_domain_decomposition.cpp @@ -3,9 +3,8 @@ #include <stdexcept> #include <arbor/distributed_context.hpp> +#include <arbor/domain_decomposition.hpp> -#include "backends.hpp" -#include "domain_decomposition.hpp" #include "hardware/node_info.hpp" #include "load_balance.hpp" #include "util/span.hpp" diff --git a/test/unit/test_event_generators.cpp b/test/unit/test_event_generators.cpp index 8d8b538ac6f66fd8090768c0eb9d25071cef5e80..6004986c6777851ee8e7e10f8560be2d993ddb75 100644 --- a/test/unit/test_event_generators.cpp +++ b/test/unit/test_event_generators.cpp @@ -1,11 +1,13 @@ #include "../gtest.h" -#include "common.hpp" -#include <event_generator.hpp> -#include <util/rangeutil.hpp> +#include <arbor/event_generator.hpp> +#include <arbor/spike_event.hpp> + +#include "util/rangeutil.hpp" + +#include "common.hpp" using namespace arb; -using pse = postsynaptic_spike_event; namespace{ pse_vector draw(event_generator& gen, time_type t0, time_type t1) { @@ -60,7 +62,7 @@ TEST(event_generators, regular) { } TEST(event_generators, seq) { - std::vector<pse> in = { + pse_vector in = { {{0, 0}, 0.1, 1.0}, {{0, 0}, 1.0, 2.0}, {{0, 0}, 1.0, 3.0}, diff --git a/test/unit/test_event_queue.cpp b/test/unit/test_event_queue.cpp index 8a3389a40d997389e1e09e087053a4664f1a1f80..3ce72e0bd7ddbe426619db582413867ebfde578d 100644 --- a/test/unit/test_event_queue.cpp +++ b/test/unit/test_event_queue.cpp @@ -3,12 +3,14 @@ #include <cmath> #include <vector> -#include <event_queue.hpp> +#include <arbor/spike_event.hpp> + +#include "event_queue.hpp" using namespace arb; TEST(event_queue, push) { - using ps_event_queue = event_queue<postsynaptic_spike_event>; + using ps_event_queue = event_queue<spike_event>; ps_event_queue q; @@ -29,7 +31,7 @@ TEST(event_queue, push) { } TEST(event_queue, pop_if_before) { - using ps_event_queue = event_queue<postsynaptic_spike_event>; + using ps_event_queue = event_queue<spike_event>; cell_member_type target[4] = { {1u, 0u}, @@ -38,7 +40,7 @@ TEST(event_queue, pop_if_before) { {2u, 3u} }; - postsynaptic_spike_event events[] = { + spike_event events[] = { {target[0], 1.f, 2.f}, {target[1], 2.f, 2.f}, {target[2], 3.f, 2.f}, diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp index 1a23165b0b21fffe5fa6e58e631223b801683022..56f122492176296361857e2e29ef5cdceb94e9e7 100644 --- a/test/unit/test_fvm_lowered.cpp +++ b/test/unit/test_fvm_lowered.cpp @@ -7,7 +7,10 @@ #include <arbor/fvm_types.hpp> #include <arbor/mc_cell.hpp> #include <arbor/mc_segment.hpp> +#include <arbor/recipe.hpp> #include <arbor/sampling.hpp> +#include <arbor/simulation.hpp> +#include <arbor/schedule.hpp> #include "algorithms.hpp" #include "backends/multicore/fvm.hpp" @@ -16,10 +19,7 @@ #include "fvm_lowered_cell_impl.hpp" #include "load_balance.hpp" #include "math.hpp" -#include "simulation.hpp" -#include "recipe.hpp" #include "sampler_map.hpp" -#include "schedule.hpp" #include "util/meta.hpp" #include "util/maputil.hpp" #include "util/rangeutil.hpp" diff --git a/test/unit/test_lif_cell_group.cpp b/test/unit/test_lif_cell_group.cpp index 40bc2c38adedeb17b94df8d115da6e3627cb5fa4..c2753e520245563707ac0ae0c92aadac0de73442 100644 --- a/test/unit/test_lif_cell_group.cpp +++ b/test/unit/test_lif_cell_group.cpp @@ -3,13 +3,15 @@ #include <arbor/distributed_context.hpp> #include <arbor/lif_cell.hpp> #include <arbor/threadinfo.hpp> +#include <arbor/recipe.hpp> +#include <arbor/simulation.hpp> #include <arbor/spike_source_cell.hpp> #include "cell_group_factory.hpp" +#include "hardware/node_info.hpp" #include "lif_cell_group.hpp" #include "load_balance.hpp" -#include "simulation.hpp" -#include "recipe.hpp" +#include "threading/threading.hpp" using namespace arb; // Simple ring network of LIF neurons. @@ -163,7 +165,7 @@ TEST(lif_cell_group, spikes) { auto decomp = partition_load_balance(recipe, nd, &context); simulation sim(recipe, decomp, &context); - std::vector<postsynaptic_spike_event> events; + std::vector<spike_event> events; // First event to trigger the spike (first neuron). events.push_back({{0, 0}, 1, 1000}); diff --git a/test/unit/test_mc_cell_group.cpp b/test/unit/test_mc_cell_group.cpp index 4aaf42ebee3ab1121ee01cacfe28230f941050c1..a9873cbcdd5acd7e2a071d3673104fa9ca5b7e32 100644 --- a/test/unit/test_mc_cell_group.cpp +++ b/test/unit/test_mc_cell_group.cpp @@ -2,7 +2,6 @@ #include <arbor/common_types.hpp> -#include "backends.hpp" #include "epoch.hpp" #include "fvm_lowered_cell.hpp" #include "mc_cell_group.hpp" diff --git a/test/unit/test_mechcat.cpp b/test/unit/test_mechcat.cpp index c6a16a61787ff94b4e2b8fe2610b4c3cbbb664cc..1c2a04bf66e3909a52e47e437b401afb8d7289eb 100644 --- a/test/unit/test_mechcat.cpp +++ b/test/unit/test_mechcat.cpp @@ -1,3 +1,4 @@ +#include <arbor/arbexcept.hpp> #include <arbor/fvm_types.hpp> #include <arbor/mechanism.hpp> #include <arbor/mechcat.hpp> @@ -194,7 +195,7 @@ TEST(mechcat, fingerprint) { EXPECT_EQ("burbleprint", cat.fingerprint("bleeble")); EXPECT_THROW(cat.register_implementation<bar_backend>("burble", make_mech<bar_backend, burble_bar>()), - std::invalid_argument); + arb::fingerprint_mismatch); } TEST(mechcat, derived_info) { @@ -245,7 +246,7 @@ TEST(mechcat, remove) { TEST(mechcat, instance) { auto cat = build_fake_catalogue(); - EXPECT_THROW(cat.instance<bar_backend>("burble"), std::invalid_argument); + EXPECT_THROW(cat.instance<bar_backend>("burble"), arb::no_such_implementation); // All fleebs on the bar backend have the same implementation: diff --git a/test/unit/test_schedule.cpp b/test/unit/test_schedule.cpp index 6f08d2bc79acc9dff6e1d1ab66a3179a7c2bccb1..0796352b6d731f71e2b2bd0b245d709fd336342d 100644 --- a/test/unit/test_schedule.cpp +++ b/test/unit/test_schedule.cpp @@ -4,8 +4,8 @@ #include <vector> #include <arbor/common_types.hpp> +#include <arbor/schedule.hpp> -#include <schedule.hpp> #include <util/partition.hpp> #include <util/rangeutil.hpp> diff --git a/test/unit/test_time_seq.cpp b/test/unit/test_time_seq.cpp index dcb87b4de0408c3e55d3bb08e13aaa9752f30837..b4166279f590602d5294a98409991bfe9d749c9a 100644 --- a/test/unit/test_time_seq.cpp +++ b/test/unit/test_time_seq.cpp @@ -1,15 +1,14 @@ #include "../gtest.h" -#include "common.hpp" #include <vector> #include <arbor/time_sequence.hpp> -#include "event_queue.hpp" #include "util/rangeutil.hpp" +#include "common.hpp" + using namespace arb; -using pse = postsynaptic_spike_event; namespace{ // Helper function that draws all samples in the half open interval diff --git a/test/validation/convergence_test.hpp b/test/validation/convergence_test.hpp index c4de6033408c1c42234ae0b726148ca1166edb13..459e788f85efabb4f438092ebc5869d87e1206b5 100644 --- a/test/validation/convergence_test.hpp +++ b/test/validation/convergence_test.hpp @@ -6,9 +6,9 @@ #include <arbor/sampling.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/simulation.hpp> +#include <arbor/schedule.hpp> -#include "simulation.hpp" -#include "schedule.hpp" #include "util/filter.hpp" #include "util/rangeutil.hpp" diff --git a/test/validation/validate_ball_and_stick.cpp b/test/validation/validate_ball_and_stick.cpp index 2520321b6dc243284084c029d48d1a5c6927c58b..6f736ff871a39b3a8cd268fe30fa9dfd7cf28a26 100644 --- a/test/validation/validate_ball_and_stick.cpp +++ b/test/validation/validate_ball_and_stick.cpp @@ -4,15 +4,16 @@ #include <arbor/common_types.hpp> #include <arbor/mc_cell.hpp> +#include <arbor/recipe.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/simulation.hpp> #include "load_balance.hpp" #include "hardware/node_info.hpp" #include "hardware/gpu.hpp" -#include "simulation.hpp" -#include "recipe.hpp" #include "util/meta.hpp" #include "util/path.hpp" +#include "util/strprintf.hpp" #include "../common_cells.hpp" #include "../simple_recipes.hpp" @@ -50,7 +51,7 @@ void run_ncomp_convergence_test( {"dt", dt}, {"sim", "arbor"}, {"units", "mV"}, - {"backend_kind", to_string(backend)} + {"backend_kind", util::to_string(backend)} }; auto exclude = stimulus_ends(c); diff --git a/test/validation/validate_compartment_policy.cpp b/test/validation/validate_compartment_policy.cpp index a2ead6c43bb8a29a9b7d520f5bed37816cfddef7..7307c459cdda933bb0ca3a85e642e30f52238625 100644 --- a/test/validation/validate_compartment_policy.cpp +++ b/test/validation/validate_compartment_policy.cpp @@ -5,11 +5,11 @@ #include <arbor/common_types.hpp> #include <arbor/mc_cell.hpp> +#include <arbor/recipe.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/simulation.hpp> -#include <simulation.hpp> -#include <recipe.hpp> -#include <util/rangeutil.hpp> +#include "util/rangeutil.hpp" #include "../gtest.h" diff --git a/test/validation/validate_kinetic.cpp b/test/validation/validate_kinetic.cpp index 063df632271c7820eb8de60c349957e3673497d7..a1d78a5682a44052bfc4bf32432ac9d90d59b86f 100644 --- a/test/validation/validate_kinetic.cpp +++ b/test/validation/validate_kinetic.cpp @@ -6,14 +6,15 @@ #include <arbor/common_types.hpp> #include <arbor/mc_cell.hpp> +#include <arbor/recipe.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/simulation.hpp> -#include <hardware/node_info.hpp> -#include <hardware/gpu.hpp> -#include <load_balance.hpp> -#include <simulation.hpp> -#include <recipe.hpp> -#include <util/rangeutil.hpp> +#include "hardware/node_info.hpp" +#include "hardware/gpu.hpp" +#include "load_balance.hpp" +#include "util/rangeutil.hpp" +#include "util/strprintf.hpp" #include "../common_cells.hpp" #include "../simple_recipes.hpp" @@ -39,7 +40,7 @@ void run_kinetic_dt( probe_label plabels[1] = {"soma.mid", {0u, 0u}}; meta["sim"] = "arbor"; - meta["backend_kind"] = to_string(backend); + meta["backend_kind"] = util::to_string(backend); convergence_test_runner<float> runner("dt", plabels, meta); runner.load_reference_data(ref_file); diff --git a/test/validation/validate_soma.cpp b/test/validation/validate_soma.cpp index e912c6b53ae56de2e4a73678babac5ed030f601f..27458680b0be4639cc638b4b51ee2aa4a6461b73 100644 --- a/test/validation/validate_soma.cpp +++ b/test/validation/validate_soma.cpp @@ -2,14 +2,15 @@ #include <arbor/common_types.hpp> #include <arbor/mc_cell.hpp> +#include <arbor/recipe.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/simulation.hpp> -#include <hardware/gpu.hpp> -#include <hardware/node_info.hpp> -#include <load_balance.hpp> -#include <simulation.hpp> -#include <recipe.hpp> -#include <util/rangeutil.hpp> +#include "hardware/gpu.hpp" +#include "hardware/node_info.hpp" +#include "load_balance.hpp" +#include "util/rangeutil.hpp" +#include "util/strprintf.hpp" #include "../common_cells.hpp" #include "../simple_recipes.hpp" @@ -41,7 +42,7 @@ void validate_soma(backend_kind backend) { {"model", "soma"}, {"sim", "arbor"}, {"units", "mV"}, - {"backend_kind", to_string(backend)} + {"backend_kind", util::to_string(backend)} }; convergence_test_runner<float> runner("dt", plabels, meta); diff --git a/test/validation/validate_synapses.cpp b/test/validation/validate_synapses.cpp index 01c237d8287aa0903d93584dd05bdecf97f8addd..b987b718bb5fdf9892a899c1923034b94fa0ed77 100644 --- a/test/validation/validate_synapses.cpp +++ b/test/validation/validate_synapses.cpp @@ -1,14 +1,15 @@ #include <nlohmann/json.hpp> #include <arbor/mc_cell.hpp> +#include <arbor/recipe.hpp> #include <arbor/simple_sampler.hpp> +#include <arbor/simulation.hpp> #include "hardware/node_info.hpp" #include "hardware/gpu.hpp" #include "load_balance.hpp" -#include "simulation.hpp" -#include "recipe.hpp" #include "util/path.hpp" +#include "util/strprintf.hpp" #include "../gtest.h" @@ -34,7 +35,7 @@ void run_synapse_test( {"model", syn_type}, {"sim", "arbor"}, {"units", "mV"}, - {"backend_kind", to_string(backend)} + {"backend_kind", util::to_string(backend)} }; mc_cell c = make_cell_ball_and_stick(false); // no stimuli @@ -42,7 +43,7 @@ void run_synapse_test( c.add_synapse({1, 0.5}, syn_default); // injected spike events - std::vector<postsynaptic_spike_event> synthetic_events = { + std::vector<spike_event> synthetic_events = { {{0u, 0u}, 10.0, 0.04}, {{0u, 0u}, 20.0, 0.04}, {{0u, 0u}, 40.0, 0.04}