diff --git a/arbor/include/arbor/morph/primitives.hpp b/arbor/include/arbor/morph/primitives.hpp index ae58c511f7eb2cb0afd076b01a3c5f5c7e1eb92a..52f9ea295b4d754682ca18b0d6aa6311e0ee4297 100644 --- a/arbor/include/arbor/morph/primitives.hpp +++ b/arbor/include/arbor/morph/primitives.hpp @@ -55,11 +55,11 @@ struct mlocation { // The relative position on the branch ∈ [0,1]. double pos; - // branch ≠npos and 0 ≤ pos ≤ 1 - friend bool test_invariants(const mlocation&); friend std::ostream& operator<<(std::ostream&, const mlocation&); }; +// branch ≠npos and 0 ≤ pos ≤ 1 +bool test_invariants(const mlocation&); ARB_DEFINE_LEXICOGRAPHIC_ORDERING(mlocation, (a.branch,a.pos), (b.branch,b.pos)); using mlocation_list = std::vector<mlocation>; diff --git a/doc/py_common.rst b/doc/py_common.rst index 5d282680e4accdb95304fdaba06712894efd2ffb..11cf1cb5882705581dd1aa255e160a4b7c178fa3 100644 --- a/doc/py_common.rst +++ b/doc/py_common.rst @@ -78,3 +78,19 @@ The types defined below are used as identifiers for cells and members of cell-lo import arbor kind = arbor.cell_kind.cable + +Morphology +---------- + +.. class:: location + + Construct a location specification with the :attr:`branch` id and the relative :attr:`position` on the branch ∈ [0.,1.], where 0. means proximal and 1. distal. + + .. attribute:: branch + + The id of the branch. + + .. attribute:: position + + The relative position (from 0., proximal, to 1., distal) on the branch. + diff --git a/doc/py_recipe.rst b/doc/py_recipe.rst index ff1ae8584e7be2e9fd19d175e57ba0c4a4201bb6..2d7f63cfe2c287180b79db99b3ca7d77a6100747 100644 --- a/doc/py_recipe.rst +++ b/doc/py_recipe.rst @@ -81,12 +81,45 @@ Details on why Arbor uses recipes and general best practices can be found in :re By default returns 0. + .. function:: num_probes(gid) + + The number of probes attached to the cell with :attr:`arbor.cell_member.gid`. + + By default returns 0. + .. function:: num_gap_junction_sites(gid) Returns the number of gap junction sites on :attr:`arbor.cell_member.gid`. By default returns 0. + .. function:: get_probe(id) + + Returns the probe(s) to allow monitoring. + + By default throws a runtime error. If :func:`num_probes` + returns a non-zero value, this must also be overridden. + +.. class:: probe + + Describes the cell probe's information. + +.. function:: cable_probe(kind, id, location) + + Returns the description of a probe at an :class:`arbor.location` on a cable cell with :attr:`id` available for monitoring data of ``voltage`` or ``current`` :attr:`kind`. + + An example of a probe on a cable cell for measuring voltage at the soma reads as follows: + + .. container:: example-code + + .. code-block:: python + + import arbor + + id = arbor.cell_member(0, 0) # cell 0, probe 0 + loc = arbor.location(0, 0) # at the soma + probe = arbor.cable_probe('voltage', id, loc) + .. class:: connection Describes a connection between two cells: @@ -388,3 +421,11 @@ helpers in cell_parameters and make_cable_cell for building cells are used. sched = arbor.explicit_schedule([1]) return [arbor.event_generator(arbor.cell_member(0,0), 0.1, sched)] return [] + + # Define one probe (for measuring voltage at the soma) on the cell. + def num_probes(self, gid): + return 1 + + def get_probe(self, id): + loc = arbor.location(0, 0) # at the soma + return arbor.cable_probe('voltage', id, loc) diff --git a/doc/py_simulation.rst b/doc/py_simulation.rst index 23b711b2f3d719650299ff0dfcb2c178c052e2fd..d2a6c5d2e12d8cef9d86dc4feca822ef1ec3bf92 100644 --- a/doc/py_simulation.rst +++ b/doc/py_simulation.rst @@ -175,3 +175,103 @@ In order to analyze the simulation output spikes can be recorded. >>> <arbor.spike: source (7,0), time 89.1529 ms> >>> <arbor.spike: source (8,0), time 101.641 ms> >>> <arbor.spike: source (9,0), time 114.125 ms> + +Recording samples +----------------- + +Definitions +*********** + +probe + A location or component of a cell that is available for monitoring (see :attr:`arbor.recipe.num_probes`, :attr:`arbor.recipe.get_probe` and :attr:`arbor.cable_probe` as references). + +sample/record + A record of data corresponding to the value at a specific *probe* at a specific time. + +sampler/sample recorder + A function that receives a sequence of *sample* records. + +Samples and sample recorders +**************************** + +In order to analyze the data collected from an :class:`arbor.probe` the samples can be recorded. + +**Types**: + +.. class:: sample + + .. attribute:: time + + The sample time [ms] at a specific probe. + + .. attribute:: value + + The sample record at a specific probe. + +.. class:: sampler + + .. function:: sampler() + + Initialize the sample recorder. + + .. function:: samples(probe_id) + + A list of the recorded samples of a probe with probe id. + +**Sampling interface**: + +.. function:: attach_sampler(sim, dt) + + Attach a sample recorder to an arbor simulation. + The recorder will record all samples from a regular sampling interval [ms] (see :class:`arbor.regular_schedule`) matching all probe ids. + +.. function:: attach_sampler(sim, dt, probe_id) + + Attach a sample recorder to an arbor simulation. + The recorder will record all samples from a regular sampling interval [ms] (see :class:`arbor.regular_schedule`) matching one probe id. + +.. container:: example-code + + .. code-block:: python + + import arbor + + # Instatitate the simulation. + sim = arbor.simulation(recipe, decomp, context) + + # Build the sample recorder on cell 0 and probe 0 with regular sampling interval of 0.1 ms + pid = arbor.cell_member(0,0) # cell 0, probe 0 + sampler = arbor.attach_sampler(sim, 0.1, pid) + + # Run the simulation for 100 ms + sim.run(100) + + # Print the sample times and values + for sa in sampler.samples(pid): + print(sa) + +>>> <arbor.sample: time 0 ms, value -65> +>>> <arbor.sample: time 0.1 ms, value -64.9981> +>>> <arbor.sample: time 0.2 ms, value -64.9967> +>>> <arbor.sample: time 0.3 ms, value -64.9956> +>>> <arbor.sample: time 0.4 ms, value -64.9947> +>>> <arbor.sample: time 0.475 ms, value -64.9941> +>>> <arbor.sample: time 0.6 ms, value -64.9932> +>>> <arbor.sample: time 0.675 ms, value -64.9927> +>>> <arbor.sample: time 0.8 ms, value -64.992> +>>> <arbor.sample: time 0.9 ms, value -64.9916> +>>> <arbor.sample: time 1 ms, value -64.9912> +>>> <arbor.sample: time 1.1 ms, value -62.936> +>>> <arbor.sample: time 1.2 ms, value -59.2284> +>>> <arbor.sample: time 1.3 ms, value -55.8485> +>>> <arbor.sample: time 1.375 ms, value -53.663> +>>> <arbor.sample: time 1.475 ms, value -51.0649> +>>> <arbor.sample: time 1.6 ms, value -47.9543> +>>> <arbor.sample: time 1.7 ms, value -45.1928> +>>> <arbor.sample: time 1.8 ms, value -41.7243> +>>> <arbor.sample: time 1.875 ms, value -38.2573> +>>> <arbor.sample: time 1.975 ms, value -31.576> +>>> <arbor.sample: time 2.1 ms, value -17.2756> +>>> <arbor.sample: time 2.2 ms, value 0.651031> +>>> <arbor.sample: time 2.275 ms, value 15.0592> + diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 063c514686850990201c865c9854ac32eecb5733..ed87571f00b035dddce3b2a66184133d1da737d4 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -22,10 +22,12 @@ add_library(pyarb MODULE domain_decomposition.cpp event_generator.cpp identifiers.cpp + morphology.cpp mpi.cpp profiler.cpp pyarb.cpp recipe.cpp + sampling.cpp schedule.cpp simulation.cpp spikes.cpp diff --git a/python/example/ring.py b/python/example/ring.py index d4710218b2e21252ecdaef8293b7db891a1f3a79..aec4f5cbaccd6eb405e569599f1b30a2f9dd326b 100644 --- a/python/example/ring.py +++ b/python/example/ring.py @@ -1,5 +1,6 @@ import sys import arbor +import matplotlib.pyplot as plt class ring_recipe (arbor.recipe): @@ -44,6 +45,13 @@ class ring_recipe (arbor.recipe): return [arbor.event_generator(arbor.cell_member(0,0), 0.1, sched)] return [] + # Define one probe (for measuring voltage at the soma) on each cell. + def num_probes(self, gid): + return 1 + + def get_probe(self, id): + loc = arbor.location(0, 0) # at the soma + return arbor.cable_probe('voltage', id, loc) context = arbor.context(threads=4, gpu_id=None) print(context) @@ -51,7 +59,7 @@ print(context) meters = arbor.meter_manager() meters.start(context) -recipe = ring_recipe(100) +recipe = ring_recipe(10) print(f'{recipe}') meters.checkpoint('recipe-create', context) @@ -71,17 +79,42 @@ print(f'{decomp}') meters.checkpoint('load-balance', context) sim = arbor.simulation(recipe, decomp, context) -print(f'{sim} finished') meters.checkpoint('simulation-init', context) -recorder = arbor.attach_spike_recorder(sim) +spike_recorder = arbor.attach_spike_recorder(sim) -sim.run(1000) +pid = arbor.cell_member(0,0) # cell 0, probe 0 +# Attach a sampler to the voltage probe on cell 0. +# Sample rate of 1 sample every ms. +sampler = arbor.attach_sampler(sim, 1, pid) + +sim.run(100) +print(f'{sim} finished') meters.checkpoint('simulation-run', context) print(f'{arbor.meter_report(meters, context)}') -for s in recorder.spikes: - print(s) +for sp in spike_recorder.spikes: + print(sp) + +print('voltage samples for probe id ', end = '') +print(pid, end = '') +print(':') + +time = [] +value = [] +for sa in sampler.samples(pid): + print(sa) + time.append(sa.time) + value.append(sa.value) + +# plot the recorded voltages over time +fig, ax = plt.subplots() +ax.plot(time, value) +ax.set(xlabel='time (ms)', ylabel='voltage (mV)', title='ring demo') +ax.legend(['voltage']) +plt.xlim(0,100) +ax.grid() +fig.savefig("voltages.png", dpi=300) diff --git a/python/morphology.cpp b/python/morphology.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b995db640c0bdd79d71bc519cb1993c0fc1bb0a --- /dev/null +++ b/python/morphology.cpp @@ -0,0 +1,33 @@ +#include <pybind11/pybind11.h> + +#include <arbor/morph/primitives.hpp> + +#include "error.hpp" +#include "strprintf.hpp" + +namespace pyarb { + +void register_morphology(pybind11::module& m) { + using namespace pybind11::literals; + + // segment location + pybind11::class_<arb::mlocation> location(m, "location", + "A location on a cable cell."); + location + .def(pybind11::init( + [](arb::msize_t branch, double pos) { + const arb::mlocation mloc{branch, pos}; + pyarb::assert_throw(arb::test_invariants(mloc), "invalid location"); + return mloc; + }), + "branch"_a, "position"_a, + "Construct a location specification holding:\n" + " branch: The id of the branch.\n" + " position: The relative position (from 0., proximal, to 1., distal) on the branch.\n") + .def_readonly("branch", &arb::mlocation::branch, "The id of the branch.") + .def_readonly("position", &arb::mlocation::pos, "The relative position on the branch (∈ [0.,1.], where 0. means proximal and 1. distal).") + .def("__str__", [](arb::mlocation l) {return util::pprintf("<arbor.location: branch {}, position {}>", l.branch, l.pos);}) + .def("__repr__", [](arb::mlocation l) {return util::pprintf("<arbor.location: branch {}, position {}>", l.branch, l.pos);}); + +} +} // namespace pyarb diff --git a/python/pyarb.cpp b/python/pyarb.cpp index 7f5bdd71529c7f51243494b3619108f63c4002a8..e17cbdfd919c6c68e948d6a4ad5b4c1cbc191703 100644 --- a/python/pyarb.cpp +++ b/python/pyarb.cpp @@ -12,8 +12,10 @@ void register_contexts(pybind11::module& m); void register_domain_decomposition(pybind11::module& m); void register_event_generators(pybind11::module& m); void register_identifiers(pybind11::module& m); +void register_morphology(pybind11::module& m); void register_profiler(pybind11::module& m); void register_recipe(pybind11::module& m); +void register_sampling(pybind11::module& m); void register_schedules(pybind11::module& m); void register_simulation(pybind11::module& m); void register_spike_handling(pybind11::module& m); @@ -33,8 +35,10 @@ PYBIND11_MODULE(arbor, m) { pyarb::register_domain_decomposition(m); pyarb::register_event_generators(m); pyarb::register_identifiers(m); + pyarb::register_morphology(m); pyarb::register_profiler(m); pyarb::register_recipe(m); + pyarb::register_sampling(m); pyarb::register_schedules(m); pyarb::register_simulation(m); pyarb::register_spike_handling(m); diff --git a/python/recipe.cpp b/python/recipe.cpp index af787726d017af98ee8a611acf5e683618d49b3a..0f50326996c34c5f7a8c126b95aad3d54214898a 100644 --- a/python/recipe.cpp +++ b/python/recipe.cpp @@ -8,10 +8,12 @@ #include <arbor/cable_cell.hpp> #include <arbor/event_generator.hpp> +#include <arbor/morph/primitives.hpp> #include <arbor/recipe.hpp> #include <arbor/spike_source_cell.hpp> #include "cells.hpp" +#include "conversion.hpp" #include "error.hpp" #include "event_generator.hpp" #include "strprintf.hpp" @@ -26,6 +28,22 @@ arb::util::unique_any py_recipe_shim::get_cell_description(arb::cell_gid_type gi return convert_cell(impl_->cell_description(gid)); } +arb::probe_info cable_probe(std::string kind, arb::cell_member_type id, arb::mlocation loc) { + arb::cell_probe_address::probe_kind pkind; + if (kind == "voltage") { + pkind = arb::cell_probe_address::probe_kind::membrane_voltage; + } + else if (kind == "current") { + pkind = arb::cell_probe_address::probe_kind::membrane_current; + } + else throw pyarb_error( + util::pprintf( + "invalid probe kind: {}, neither voltage nor current", kind)); + + arb::cell_probe_address probe{loc, pkind}; + return arb::probe_info{id, pkind, probe}; +}; + std::vector<arb::event_generator> py_recipe_shim::event_generators(arb::cell_gid_type gid) const { using namespace std::string_literals; using pybind11::isinstance; @@ -135,7 +153,6 @@ void register_recipe(pybind11::module& m) { .def("num_targets", &py_recipe::num_targets, "gid"_a, "The number of post-synaptic sites on gid, 0 by default.") - // TODO: py_recipe::num_probes .def("num_gap_junction_sites", &py_recipe::num_gap_junction_sites, "gid"_a, "The number of gap junction sites on gid, 0 by default.") @@ -148,9 +165,25 @@ void register_recipe(pybind11::module& m) { .def("gap_junctions_on", &py_recipe::gap_junctions_on, "gid"_a, "A list of the gap junctions connected to gid, [] by default.") - // TODO: py_recipe::get_probe + .def("num_probes", &py_recipe::num_probes, + "gid"_a, + "The number of probes on gid, 0 by default.") + .def("get_probe", &py_recipe::get_probe, + "id"_a, + "The probe(s) to allow monitoring, must be provided if num_probes() returns a non-zero value.") // TODO: py_recipe::global_properties .def("__str__", [](const py_recipe&){return "<arbor.recipe>";}) .def("__repr__", [](const py_recipe&){return "<arbor.recipe>";}); + + // Probes + m.def("cable_probe", &cable_probe, + "Description of a probe at a location on a cable cell with id available for monitoring data of kind "\ + "where kind is one of 'voltage' or 'current'.", + "kind"_a, "id"_a, "location"_a); + + pybind11::class_<arb::probe_info> probe(m, "probe"); + probe + .def("__repr__", [](const arb::probe_info& p){return util::pprintf("<arbor.probe: cell {}, probe {}>", p.id.gid, p.id.index);}) + .def("__str__", [](const arb::probe_info& p){return util::pprintf("<arbor.probe: cell {}, probe {}>", p.id.gid, p.id.index);}); } } // namespace pyarb diff --git a/python/recipe.hpp b/python/recipe.hpp index 3ee71adbb2004e4c374436643869cd5f97581416..3e3492424701795493df5780863b8320bd38538f 100644 --- a/python/recipe.hpp +++ b/python/recipe.hpp @@ -10,6 +10,9 @@ #include <arbor/cable_cell_param.hpp> #include <arbor/recipe.hpp> +#include "error.hpp" +#include "strprintf.hpp" + namespace pyarb { // pyarb::recipe is the recipe interface used by Python. @@ -48,9 +51,12 @@ public: virtual std::vector<arb::gap_junction_connection> gap_junctions_on(arb::cell_gid_type) const { return {}; } - - //TODO: virtual arb::cell_size_type num_probes(arb::cell_gid_type) const { return 0; } - //TODO: virtual pybind11::object get_probe (arb::cell_member_type id) const {...} + virtual arb::cell_size_type num_probes(arb::cell_gid_type) const { + return 0; + } + virtual arb::probe_info get_probe (arb::cell_member_type id) const { + throw pyarb_error(util::pprintf("bad probe id {}", id)); + } //TODO: virtual pybind11::object global_properties(arb::cell_kind kind) const {return pybind11::none();}; }; @@ -92,8 +98,13 @@ public: PYBIND11_OVERLOAD(std::vector<arb::gap_junction_connection>, py_recipe, gap_junctions_on, gid); } - //TODO: arb::cell_size_type num_probes(arb::cell_gid_type) - //TODO: pybind11::object get_probe(arb::cell_member_type id) + arb::cell_size_type num_probes(arb::cell_gid_type gid) const override { + PYBIND11_OVERLOAD(arb::cell_size_type, py_recipe, num_probes, gid); + } + + arb::probe_info get_probe(arb::cell_member_type id) const override { + PYBIND11_OVERLOAD(arb::probe_info, py_recipe, get_probe, id); + } }; // A recipe shim that holds a pyarb::recipe implementation. @@ -131,8 +142,6 @@ public: return impl_->num_targets(gid); } - //TODO: arb::cell_size_type num_probes(arb::cell_gid_type gid) - arb::cell_size_type num_gap_junction_sites(arb::cell_gid_type gid) const override { return impl_->num_gap_junction_sites(gid); } @@ -147,7 +156,13 @@ public: return impl_->gap_junctions_on(gid); } - //TODO: arb::probe_info get_probe(arb::cell_member_type id) + arb::cell_size_type num_probes(arb::cell_gid_type gid) const override { + return impl_->num_probes(gid); + } + + arb::probe_info get_probe(arb::cell_member_type id) const override { + return impl_->get_probe(id); + } // TODO: wrap arb::util::any get_global_properties(arb::cell_kind kind) const override { diff --git a/python/sampling.cpp b/python/sampling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e281c2c2f5e534f245f69250c488ea25c5234677 --- /dev/null +++ b/python/sampling.cpp @@ -0,0 +1,157 @@ +#include <mutex> + +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +#include <arbor/common_types.hpp> +#include <arbor/sampling.hpp> +#include <arbor/simulation.hpp> + +#include "error.hpp" +#include "strprintf.hpp" + +namespace pyarb { + +// TODO: trace entry of different types/container (e.g. vector of doubles to get all samples of a cell) + +struct trace_entry { + arb::time_type t; + double v; +}; + +// A helper struct (state) ensuring that only one thread can write to the probe_buffers holding the trace entries (mapped by probe id) +struct sampler_state { + std::mutex mutex; + std::unordered_map<arb::cell_member_type, std::vector<trace_entry>> probe_buffers; + + std::vector<trace_entry>& probe_buffer(arb::cell_member_type pid) { + // lock the mutex, s.t. other threads cannot write + std::lock_guard<std::mutex> lock(mutex); + // return or create entry + return probe_buffers[pid]; + } + + // helper function to search probe id in probe_buffers + bool has_pid(arb::cell_member_type pid) { + return probe_buffers.count(pid); + } + + // helper function to push back to locked vector + void push_back(arb::cell_member_type pid, trace_entry value) { + auto& v = probe_buffer(pid); + v.push_back(std::move(value)); + } + + // Access the probe buffers + const std::unordered_map<arb::cell_member_type, std::vector<trace_entry>>& samples() const { + return probe_buffers; + } +}; + +// A functor that models arb::sampler_function. +// Holds a shared pointer to the trace_entry used to store the samples, so that if +// the trace_entry in sampler is garbage collected in Python, stores will +// not seg fault. + +struct sample_callback { + std::shared_ptr<sampler_state> sample_store; + + sample_callback(const std::shared_ptr<sampler_state>& state): + sample_store(state) + {} + + void operator() (arb::cell_member_type probe_id, arb::probe_tag tag, std::size_t n, const arb::sample_record* recs) { + auto& v = sample_store->probe_buffer(probe_id); + for (std::size_t i = 0; i<n; ++i) { + if (auto p = arb::util::any_cast<const double*>(recs[i].data)) { + v.push_back({recs[i].time, *p}); + } + else { + throw std::runtime_error("unexpected sample type"); + } + } + }; +}; + +// Helper type for recording samples from a simulation. +// This type is wrapped in Python, to expose sampler::sample_store. +struct sampler { + std::shared_ptr<sampler_state> sample_store; + + sample_callback callback() { + // initialize the sample_store + sample_store = std::make_shared<sampler_state>(); + + // The callback holds a copy of sample_store, i.e. the shared + // pointer is held by both the sampler and the callback, so if + // the sampler is destructed in the calling Python code, attempts + // to write to sample_store inside the callback will not seg fault. + return sample_callback(sample_store); + } + + const std::vector<trace_entry>& samples(arb::cell_member_type pid) const { + if (!sample_store->has_pid(pid)) { + throw std::runtime_error(util::pprintf("probe id {} does not exist", pid)); + } + return sample_store->probe_buffer(pid); + } + + void clear() { + for (auto b: sample_store->probe_buffers) { + b.second.clear(); + } + } +}; + +// Adds sampler to one probe with pid +std::shared_ptr<sampler> attach_sampler(arb::simulation& sim, arb::time_type interval, arb::cell_member_type pid) { + auto r = std::make_shared<sampler>(); + sim.add_sampler(arb::one_probe(pid), arb::regular_schedule(interval), r->callback()); + return r; +} + +// Adds sampler to all probes +std::shared_ptr<sampler> attach_sampler(arb::simulation& sim, arb::time_type interval) { + auto r = std::make_shared<sampler>(); + sim.add_sampler(arb::all_probes, arb::regular_schedule(interval), r->callback()); + return r; +} + +std::string sample_str(const trace_entry& s) { + return util::pprintf("<arbor.sample: time {} ms, \tvalue {}>", s.t, s.v); +} + +void register_sampling(pybind11::module& m) { + using namespace pybind11::literals; + + // Sample + pybind11::class_<trace_entry> sample(m, "sample"); + sample + .def_readonly("time", &trace_entry::t, "The sample time [ms] at a specific probe.") + .def_readonly("value", &trace_entry::v, "The sample record at a specific probe.") + .def("__str__", &sample_str) + .def("__repr__", &sample_str); + + // Sampler + pybind11::class_<sampler, std::shared_ptr<sampler>> samplerec(m, "sampler"); + samplerec + .def(pybind11::init<>()) + .def("samples", &sampler::samples, + "A list of the recorded samples of a probe with probe id.", + "probe_id"_a) + .def("clear", &sampler::clear, "Clear all recorded samples."); + + m.def("attach_sampler", + (std::shared_ptr<sampler> (*)(arb::simulation&, arb::time_type)) &attach_sampler, + "Attach a sample recorder to an arbor simulation.\n" + "The recorder will record all samples from a regular sampling interval [ms] matching all probe ids.", + "sim"_a, "dt"_a); + + m.def("attach_sampler", + (std::shared_ptr<sampler> (*)(arb::simulation&, arb::time_type, arb::cell_member_type)) &attach_sampler, + "Attach a sample recorder to an arbor simulation.\n" + "The recorder will record all samples from a regular sampling interval [ms] matching one probe id.", + "sim"_a, "dt"_a, "probe_id"_a); +} + +} // namespace pyarb diff --git a/python/simulation.cpp b/python/simulation.cpp index df6244bb399d5877f543a331f129999e1263333d..60137614ae81f1c3a5bc0fb0f15a39a46aeb48bf 100644 --- a/python/simulation.cpp +++ b/python/simulation.cpp @@ -1,8 +1,11 @@ #include <pybind11/pybind11.h> +#include <arbor/common_types.hpp> +#include <arbor/sampling.hpp> #include <arbor/simulation.hpp> #include "context.hpp" +#include "error.hpp" #include "recipe.hpp" namespace pyarb { diff --git a/test/validation/validate_kinetic.cpp b/test/validation/validate_kinetic.cpp index 57ddd135174af948de68699bccdfa0ff36808e60..e855d02187d5f203dc74a6108255112fe37b8f0b 100644 --- a/test/validation/validate_kinetic.cpp +++ b/test/validation/validate_kinetic.cpp @@ -91,7 +91,7 @@ void validate_kinetic_kinlva(const arb::context& ctx) { // 20 µm diameter soma with single mechanism, current probe cable_cell c; auto soma = c.add_soma(10); - c.add_stimulus({0,0.5}, {20., 130., -0.025}); + c.place(mlocation{0,0.5}, {20., 130., -0.025}); soma->add_mechanism("test_kinlva"); cell_probe_address probe{{0, 0.5}, cell_probe_address::membrane_voltage}; diff --git a/test/validation/validate_synapses.cpp b/test/validation/validate_synapses.cpp index 1fd4de8c9020bfeb96b391c26e74881036d2ef60..daa3a209ff9fb98a53b88884b58aa4f6d98ff885 100644 --- a/test/validation/validate_synapses.cpp +++ b/test/validation/validate_synapses.cpp @@ -40,7 +40,7 @@ void run_synapse_test( cable_cell c = make_cell_ball_and_stick(false); // no stimuli mechanism_desc syn_default(syn_type); - c.add_synapse({1, 0.5}, syn_default); + c.place(mlocation{1, 0.5}, syn_default); // injected spike events std::vector<spike_event> synthetic_events = {