diff --git a/arbor/include/arbor/simulation.hpp b/arbor/include/arbor/simulation.hpp index 0559f40f257cd3032e67ea17996e00f98c55e01d..7e11671935f2957603452f12f828b66215a22d38 100644 --- a/arbor/include/arbor/simulation.hpp +++ b/arbor/include/arbor/simulation.hpp @@ -18,6 +18,7 @@ namespace arb { using spike_export_function = std::function<void(const std::vector<spike>&)>; +using epoch_function = std::function<void(double time, double tfinal)>; // simulation_state comprises private implementation for simulation class. class simulation_state; @@ -57,6 +58,10 @@ public: // spike vector. void set_local_spike_callback(spike_export_function = spike_export_function{}); + // Register a callback that will be called at the end of each epoch, and at the + // start of the simulation. + void set_epoch_callback(epoch_function = epoch_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. @@ -68,4 +73,7 @@ private: std::unique_ptr<simulation_state> impl_; }; +// An epoch callback function that prints out a text progress bar. +ARB_ARBOR_API epoch_function epoch_progress_bar(); + } // namespace arb diff --git a/arbor/simulation.cpp b/arbor/simulation.cpp index f0a6cccaa303304dbd83d567c1522a2e9ca50bdb..940de4ba2b9d8a55f664440f48138fd757eb54db 100644 --- a/arbor/simulation.cpp +++ b/arbor/simulation.cpp @@ -115,6 +115,7 @@ public: spike_export_function global_export_callback_; spike_export_function local_export_callback_; + epoch_function epoch_callback_; private: // Record last computed epoch (integration interval). @@ -407,10 +408,13 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { epoch current = next_epoch(prev, t_interval_); epoch next = next_epoch(current, t_interval_); + if (epoch_callback_) epoch_callback_(current.t0, tfinal); + if (next.empty()) { enqueue(current); update(current); exchange(current); + if (epoch_callback_) epoch_callback_(current.t1, tfinal); } else { enqueue(current); @@ -418,6 +422,7 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { g.run([&]() { enqueue(next); }); g.run([&]() { update(current); }); g.wait(); + if (epoch_callback_) epoch_callback_(current.t1, tfinal); for (;;) { prev = current; @@ -428,6 +433,7 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { g.run([&]() { exchange(prev); enqueue(next); }); g.run([&]() { update(current); }); g.wait(); + if (epoch_callback_) epoch_callback_(current.t1, tfinal); } g.run([&]() { exchange(prev); }); @@ -435,6 +441,7 @@ time_type simulation_state::run(time_type tfinal, time_type dt) { g.wait(); exchange(current); + if (epoch_callback_) epoch_callback_(current.t1, tfinal); } // Record current epoch for next run() invocation. @@ -558,10 +565,47 @@ void simulation::set_local_spike_callback(spike_export_function export_callback) impl_->local_export_callback_ = std::move(export_callback); } +void simulation::set_epoch_callback(epoch_function epoch_callback) { + impl_->epoch_callback_ = std::move(epoch_callback); +} + void simulation::inject_events(const cse_vector& events) { impl_->inject_events(events); } simulation::~simulation() = default; +ARB_ARBOR_API epoch_function epoch_progress_bar() { + struct impl { + double t0 = 0; + bool first = true; + + void operator() (double t, double tfinal) { + constexpr unsigned bar_width = 50; + static const std::string bar_buffer(bar_width+1, '-'); + + if (first) { + first = false; + t0 = t; + } + + double percentage = (tfinal==t0)? 1: (t-t0)/(tfinal-t0); + int val = percentage * 100; + int lpad = percentage * bar_width; + int rpad = bar_width - lpad; + printf("\r%3d%% |%.*s%*s| %12ums", val, lpad, bar_buffer.c_str(), rpad, "", (unsigned)t); + + if (t==tfinal) { + // Print new line and reset counters on the last step. + printf("\n"); + t0 = tfinal; + first = true; + } + fflush(stdout); + } + }; + + return impl{}; +} + } // namespace arb diff --git a/doc/python/simulation.rst b/doc/python/simulation.rst index a49315038873ed1f7fab0e7c7c2c8f27b843e46d..7d8b785107d223289aac71d24360f1ae2dd6bf7b 100644 --- a/doc/python/simulation.rst +++ b/doc/python/simulation.rst @@ -156,6 +156,10 @@ over the local and distributed hardware resources (see :ref:`pydomdec`). Then, t be a NumPy array, with the first column corresponding to sample time and subsequent columns holding the value or values that were sampled from that probe at that time. + .. function:: progress_banner() + + Print a progress bar during simulation, with elapsed miliseconds and percentage of simulation completed. + **Types:** .. class:: binning diff --git a/example/drybench/CMakeLists.txt b/example/drybench/CMakeLists.txt index c407f461d211c3e00b02522c95381b4c472e0fc0..f9cecd75c54b60da294781b68341688bcdd04503 100644 --- a/example/drybench/CMakeLists.txt +++ b/example/drybench/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(drybench EXCLUDE_FROM_ALL drybench.cpp) add_dependencies(examples drybench) -target_link_libraries(drybench PRIVATE arbor arborenv arbor-sup ext-json) +target_link_libraries(drybench PRIVATE arbor arborenv arbor-sup ${json_library_name}) diff --git a/example/ring/ring.cpp b/example/ring/ring.cpp index 8e66f2ef2ea9fbb1547d17ab8965768ef42bf5a4..45a3b55be009b6e2dc7e5a66b24ab23451ac916e 100644 --- a/example/ring/ring.cpp +++ b/example/ring/ring.cpp @@ -44,9 +44,9 @@ struct ring_params { ring_params() = default; std::string name = "default"; - unsigned num_cells = 10; + unsigned num_cells = 100; double min_delay = 10; - double duration = 200; + double duration = 1000; cell_parameters cell; }; @@ -187,7 +187,10 @@ int main(int argc, char** argv) { meters.checkpoint("model-init", context); - std::cout << "running simulation" << std::endl; + if (root) { + sim.set_epoch_callback(arb::epoch_progress_bar()); + } + std::cout << "running simulation\n" << std::endl; // Run the simulation for 100 ms, with time steps of 0.025 ms. sim.run(params.duration, 0.025); diff --git a/python/example/network_ring.py b/python/example/network_ring.py index 6794d2381524090dbfb50bc6132559cefa7544d4..953b31b169c001b3c0a11361fd1b0bbe5cdc4843 100755 --- a/python/example/network_ring.py +++ b/python/example/network_ring.py @@ -120,6 +120,7 @@ sim.record(arbor.spike_recording.all) handles = [sim.sample((gid, 0), arbor.regular_schedule(0.1)) for gid in range(ncells)] # (15) Run simulation for 100 ms +sim.progress_banner() sim.run(100) print('Simulation finished') diff --git a/python/simulation.cpp b/python/simulation.cpp index fecae322a987aac894fc02c2af0eb8be30caaba9..b506ff17b96f511c70d2ca77afdc3f557ef05706 100644 --- a/python/simulation.cpp +++ b/python/simulation.cpp @@ -169,6 +169,10 @@ public: return py::list{}; } } + + void progress_banner() { + sim_->set_epoch_callback(arb::epoch_progress_bar()); + } }; void register_simulation(pybind11::module& m, pyarb_global_ptr global_ptr) { @@ -236,7 +240,9 @@ void register_simulation(pybind11::module& m, pyarb_global_ptr global_ptr) { "Remove sampling associated with the given handle.", "handle"_a) .def("remove_all_samplers", &simulation_shim::remove_sampler, - "Remove all sampling on the simulatr."); + "Remove all sampling on the simulatr.") + .def("progress_banner", &simulation_shim::progress_banner, + "Show a text progress bar during simulation."); }