From 40612fa727fab4497c7b5fdfbf7ac2cfc15f0e06 Mon Sep 17 00:00:00 2001 From: Sam Yates <yates@cscs.ch> Date: Thu, 5 Jul 2018 08:47:18 +0200 Subject: [PATCH] Feature/lib install target part 3 (#518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This time we're moving `recipe.hpp` and `simulation.hpp`, plus the requirements they bring. Code changes: * Pimplize `simulation`. * Consolidate arbor exceptions: all non-cell kind specific exceptions that might be expected to reach user code now have consistent messages and fit in an exception hierarchy based at `arb::arbor_exception`. Internal errors throw an `arb::arbor_internal_error` exception. * Renamed `postsynaptic_spike_event` to `spike_event`. (Note: `pse_vector` name is unchanged.) * Repurposed `pprintf` and moved it into `strprintf.h` — further consolidation is a TODO. * Made a generic `util::to_string` to avoid redundancy of `operator<<` overloads and other `to_string` definitions. Defaults to ADL `to_string`, `std::to_string`, and finally tries using `operator<<`. --- arbor/CMakeLists.txt | 2 + arbor/arbexcept.cpp | 74 +++++ arbor/backends.hpp | 22 -- arbor/backends/gpu/mechanism.cpp | 11 +- arbor/backends/gpu/multi_event_stream.hpp | 4 +- arbor/backends/gpu/threshold_watcher.hpp | 3 +- arbor/backends/multicore/mechanism.cpp | 10 +- .../backends/multicore/multi_event_stream.hpp | 5 +- arbor/benchmark_cell_group.cpp | 8 +- arbor/benchmark_cell_group.hpp | 7 +- arbor/cell_group.hpp | 6 +- arbor/cell_group_factory.cpp | 22 +- arbor/cell_group_factory.hpp | 8 +- arbor/common_types_io.cpp | 18 +- arbor/communication/communicator.hpp | 4 +- arbor/connection.hpp | 2 +- arbor/event_binner.cpp | 3 +- arbor/event_binner.hpp | 6 - arbor/event_queue.hpp | 32 +-- arbor/fvm_layout.cpp | 16 +- arbor/fvm_lowered_cell.hpp | 12 +- arbor/fvm_lowered_cell_impl.cpp | 6 +- arbor/fvm_lowered_cell_impl.hpp | 33 ++- arbor/lif_cell_group.cpp | 1 + arbor/lif_cell_group.hpp | 7 +- arbor/load_balance.hpp | 6 +- arbor/mc_cell.cpp | 12 +- arbor/mc_cell_group.cpp | 2 +- arbor/mc_cell_group.hpp | 4 +- arbor/mechcat.cpp | 21 +- arbor/merge_events.cpp | 13 +- arbor/merge_events.hpp | 15 +- arbor/partition_load_balance.cpp | 5 +- arbor/sampler_map.hpp | 2 +- arbor/schedule.cpp | 3 +- arbor/simulation.cpp | 258 ++++++++++++------ arbor/simulation.hpp | 104 ------- arbor/spike_event_io.cpp | 11 + arbor/spike_source_cell_group.cpp | 4 +- arbor/spike_source_cell_group.hpp | 8 +- arbor/threading/threading.cpp | 6 +- arbor/util/pprintf.hpp | 49 ---- arbor/util/strprintf.hpp | 68 ++++- example/bench/bench.cpp | 11 +- example/bench/recipe.hpp | 3 +- example/brunel/brunel_miniapp.cpp | 6 +- example/brunel/partitioner.hpp | 9 +- example/generators/event_gen.cpp | 6 +- example/miniapp/miniapp.cpp | 8 +- example/miniapp/miniapp_recipes.cpp | 6 +- example/miniapp/miniapp_recipes.hpp | 3 +- include/arbor/arbexcept.hpp | 98 +++++++ include/arbor/common_types.hpp | 22 +- .../arbor}/domain_decomposition.hpp | 14 +- {arbor => include/arbor}/event_generator.hpp | 46 ++-- {arbor => include/arbor}/generic_event.hpp | 0 include/arbor/mc_cell.hpp | 20 +- {arbor => include/arbor}/recipe.hpp | 14 +- {arbor => include/arbor}/schedule.hpp | 15 +- include/arbor/simulation.hpp | 63 +++++ include/arbor/spike_event.hpp | 31 +++ include/arbor/util/compat.hpp | 4 +- {arbor => include/arbor}/util/handle_set.hpp | 0 include/arbor/util/uninitialized.hpp | 8 +- test/common_cells.hpp | 3 +- test/simple_recipes.hpp | 5 +- test/ubench/event_binning.cpp | 14 +- test/ubench/event_setup.cpp | 16 +- test/unit-distributed/test_communicator.cpp | 17 +- test/unit/test_backend.cpp | 2 +- test/unit/test_domain_decomposition.cpp | 3 +- test/unit/test_event_generators.cpp | 12 +- test/unit/test_event_queue.cpp | 10 +- test/unit/test_fvm_lowered.cpp | 6 +- test/unit/test_lif_cell_group.cpp | 8 +- test/unit/test_mc_cell_group.cpp | 1 - test/unit/test_mechcat.cpp | 5 +- test/unit/test_schedule.cpp | 2 +- test/unit/test_time_seq.cpp | 5 +- test/validation/convergence_test.hpp | 4 +- test/validation/validate_ball_and_stick.cpp | 7 +- .../validate_compartment_policy.cpp | 6 +- test/validation/validate_kinetic.cpp | 15 +- test/validation/validate_soma.cpp | 15 +- test/validation/validate_synapses.cpp | 9 +- 85 files changed, 886 insertions(+), 579 deletions(-) create mode 100644 arbor/arbexcept.cpp delete mode 100644 arbor/backends.hpp delete mode 100644 arbor/simulation.hpp create mode 100644 arbor/spike_event_io.cpp delete mode 100644 arbor/util/pprintf.hpp create mode 100644 include/arbor/arbexcept.hpp rename {arbor => include/arbor}/domain_decomposition.hpp (85%) rename {arbor => include/arbor}/event_generator.hpp (84%) rename {arbor => include/arbor}/generic_event.hpp (100%) rename {arbor => include/arbor}/recipe.hpp (88%) rename {arbor => include/arbor}/schedule.hpp (92%) create mode 100644 include/arbor/simulation.hpp create mode 100644 include/arbor/spike_event.hpp rename {arbor => include/arbor}/util/handle_set.hpp (100%) diff --git a/arbor/CMakeLists.txt b/arbor/CMakeLists.txt index 8208a98e..4ea56fd8 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 00000000..a8b589b7 --- /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 e45f96e0..00000000 --- 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 8e6d7bae..cb57c95a 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 4498b87c..b03e374c 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 09f56275..7cc62f8a 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 3fbb4fb1..a5b22f3e 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 2a761b8c..aa0bea06 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 6466d7c1..0556045d 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 146baf7f..02fee8d5 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 ed53924f..98cf20ab 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 96079522..54e343ea 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 88770475..320e4ff5 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 2e46ddd7..c5b90478 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 d9a6f759..78aa7e4c 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 40d19287..e4fbf150 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 7fcdbbac..6301a349 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 25ff4f0b..c1bcd72c 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 0a5a24d9..b785acd0 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 d634fc71..6f3d1ee9 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 3b22c5ff..81a26887 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 fe317e93..1136f64e 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 f8a7f6b9..b07fcfea 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 70f51250..7cce6cc5 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 d81aaf3b..6ed49d39 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 0dbb0cf4..757f5a31 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 2184134c..5807afd0 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 b5e12cc2..e03ae795 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 463e59d0..be9599d7 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 5ce86884..ed314a78 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 4e5d8600..096dd169 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 72c3918d..eb058fec 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 062373de..c9a18ec2 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 08040015..5d137fbf 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 18880296..2bfc246e 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 5134e714..8339b0c0 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 d5be946e..00000000 --- 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 00000000..f035ae86 --- /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 d62ac2c2..3859ba0c 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 da714570..7169fb08 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 9f985503..6e2ab43e 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 f93d58f5..00000000 --- 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 738353f9..f548e79b 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 4f5bff6a..1ac4dcf1 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 f06952be..4c8c8102 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 bb2af814..ef07bf2c 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 1aaae53a..cd4d383d 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 5a21764d..22607137 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 6f43f54a..2fb8be9c 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 dc250abc..50838d1a 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 684b58b8..94103c1f 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 00000000..d136362a --- /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 aed91121..923cf6b6 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 b225970d..aecf5a83 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 048b62eb..e18290d7 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 b9e4b101..71e65794 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 8d163c35..00147ff8 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 66086a84..15ddadae 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 00000000..c7844e93 --- /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 00000000..2b72314c --- /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 7784f047..0603e02c 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 a94b3c62..5c8e46e2 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 3ced9dcb..134395ca 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 531c66d8..92c39208 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 94b58eba..1c33cfa7 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 8d5cf3a1..9931e8ba 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 151df0f3..3c3daf17 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 5e1c18d4..74db35c2 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 16dd8785..dc27a433 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 8d8b538a..6004986c 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 8a3389a4..3ce72e0b 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 1a23165b..56f12249 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 40bc2c38..c2753e52 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 4aaf42eb..a9873cbc 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 c6a16a61..1c2a04bf 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 6f08d2bc..0796352b 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 dcb87b4d..b4166279 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 c4de6033..459e788f 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 2520321b..6f736ff8 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 a2ead6c4..7307c459 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 063df632..a1d78a56 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 e912c6b5..27458680 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 01c237d8..b987b718 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} -- GitLab