diff --git a/example/dryrun/dryrun.cpp b/example/dryrun/dryrun.cpp
index 0ef5193d1aa5038ace8653cc9ee2177a3ee4fdb3..54757996001288c742fce0bf2c1dca343f078ad2 100644
--- a/example/dryrun/dryrun.cpp
+++ b/example/dryrun/dryrun.cpp
@@ -18,6 +18,7 @@
 #include <arbor/morph/primitives.hpp>
 #include <arbor/profile/meter_manager.hpp>
 #include <arbor/profile/profiler.hpp>
+#include <arbor/simple_sampler.hpp>
 #include <arbor/simulation.hpp>
 #include <arbor/symmetric_recipe.hpp>
 #include <arbor/recipe.hpp>
@@ -44,6 +45,7 @@ struct run_params {
     cell_parameters cell;
 };
 
+void write_trace_json(const arb::trace_data<double>& trace);
 run_params read_options(int argc, char** argv);
 
 using arb::cell_gid_type;
@@ -115,7 +117,7 @@ public:
     std::vector<arb::event_generator> event_generators(cell_gid_type gid) const override {
         std::vector<arb::event_generator> gens;
         if (gid%20 == 0) {
-            gens.push_back(arb::explicit_generator(arb::pse_vector{{{gid, 0}, 0.1, 1.0}}));
+            gens.push_back(arb::explicit_generator(arb::pse_vector{{{gid, 0}, 1.0, event_weight_}}));
         }
         return gens;
     }
@@ -130,7 +132,7 @@ private:
     cell_size_type num_tiles_;
     cell_parameters cell_params_;
     double min_delay_;
-    float event_weight_ = 0.01;
+    float event_weight_ = 0.05;
 };
 
 int main(int argc, char** argv) {
@@ -186,6 +188,15 @@ int main(int argc, char** argv) {
         // Construct the model.
         arb::simulation sim(recipe, decomp, ctx);
 
+        // The id of the only probe on the cell: the cell_member type points to (cell 0, probe 0)
+        auto probe_id = cell_member_type{0, 0};
+        // The schedule for sampling is 10 samples every 1 ms.
+        auto sched = arb::regular_schedule(1);
+        // This is where the voltage samples will be stored as (time, value) pairs
+        arb::trace_vector<double> voltage;
+        // Now attach the sampler at probe_id, with sampling schedule sched, writing to voltage
+        sim.add_sampler(arb::one_probe(probe_id), sched, arb::make_simple_sampler(voltage));
+
         // Set up recording of spikes to a vector on the root process.
         std::vector<arb::spike> recorded_spikes;
         if (root) {
@@ -221,6 +232,8 @@ int main(int argc, char** argv) {
                     fid.write(linebuf, n);
                 }
             }
+            // Write the samples to a json file.
+            write_trace_json(voltage.at(0));
         }
 
         auto profile = arb::profile::profiler_summary();
@@ -237,6 +250,27 @@ int main(int argc, char** argv) {
     return 0;
 }
 
+void write_trace_json(const arb::trace_data<double>& trace) {
+    std::string path = "./voltages.json";
+
+    nlohmann::json json;
+    json["name"] = "ring demo";
+    json["units"] = "mV";
+    json["cell"] = "0.0";
+    json["probe"] = "0";
+
+    auto& jt = json["data"]["time"];
+    auto& jy = json["data"]["voltage"];
+
+    for (const auto& sample: trace) {
+        jt.push_back(sample.t);
+        jy.push_back(sample.v);
+    }
+
+    std::ofstream file(path);
+    file << std::setw(1) << json << "\n";
+}
+
 run_params read_options(int argc, char** argv) {
     using sup::param_from_json;
 
diff --git a/python/example/ring.py b/python/example/ring.py
index 8ddaaf54e31d0efcc2d48f76bb43aee6f31c8d8c..17d01f1033abc998ccfdd6a8de5cda230c357077 100644
--- a/python/example/ring.py
+++ b/python/example/ring.py
@@ -33,12 +33,12 @@ def make_cable_cell(gid):
     cell = b.build()
 
     # Put hh dynamics on soma, and passive properties on the dendrites.
-    cell.paint('soma', 'hh')
-    cell.paint('dend', 'pas')
+    cell.paint('"soma"', 'hh')
+    cell.paint('"dend"', 'pas')
     # Attach a single synapse.
-    cell.place('synapse_site', 'expsyn')
+    cell.place('"synapse_site"', 'expsyn')
     # Attach a spike detector with threshold of -10 mV.
-    cell.place('root', arbor.spike_detector(-10))
+    cell.place('"root"', arbor.spike_detector(-10))
 
     return cell
 
@@ -84,13 +84,9 @@ 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):
+    def get_probes(self, gid):
         loc = arbor.location(0, 0) # at the soma
-        return arbor.cable_probe('voltage', id, loc)
+        return [arbor.cable_probe('voltage', loc)]
 
 context = arbor.context(threads=12, gpu_id=None)
 print(context)
diff --git a/python/example/single_cell_builder.py b/python/example/single_cell_builder.py
index 58e7054cacfa12343c85f0f390a00400cedb36bd..98ce339b22737f725fc2cd0ee0f82f2e6f9f54f6 100644
--- a/python/example/single_cell_builder.py
+++ b/python/example/single_cell_builder.py
@@ -64,7 +64,7 @@ cell.place('"stim_site"', arbor.iclamp( 10, 2, 0.8))
 cell.place('"stim_site"', arbor.iclamp( 50, 2, 0.8))
 cell.place('"stim_site"', arbor.iclamp( 80, 2, 0.8))
 # Add a spike detector with threshold of -10 mV.
-cell.place('root', arbor.spike_detector(-10))
+cell.place('"root"', arbor.spike_detector(-10))
 
 # Make single cell model.
 m = arbor.single_cell_model(cell)
diff --git a/python/example/single_cell_swc.py b/python/example/single_cell_swc.py
index 957dc8bfa978cd745d4047cdf687102d3ecc09c3..f50768bb6e7c384d345e4b833595ef3d6e8aab9d 100644
--- a/python/example/single_cell_swc.py
+++ b/python/example/single_cell_swc.py
@@ -14,9 +14,7 @@ from arbor import location as loc
 import matplotlib.pyplot as plt
 
 # Load a cell morphology from an swc file.
-# The model has 31 branches, including soma, dendrites and axon.
-#tree = arbor.load_swc('../../test/unit/swc/example.swc')
-tree = arbor.load_swc('example.swc')
+tree = arbor.load_swc('../../test/unit/swc/pyramidal.swc')
 
 # Define the regions and locsets in the model.
 defs = {'soma': '(tag 1)',  # soma has tag 1 in swc files.
@@ -30,7 +28,7 @@ labels = arbor.label_dict(defs)
 # Combine morphology with region and locset definitions to make a cable cell.
 cell = arbor.cable_cell(tree, labels)
 
-print(cell.locations('axon_end'))
+print(cell.locations('"axon_end"'))
 
 # Set initial membrane potential to -55 mV
 cell.set_properties(Vm=-55)
@@ -44,8 +42,8 @@ cell.paint('"dend"', 'pas')
 # Increase resistivity on dendrites.
 cell.paint('"dend"', rL=500)
 # Attach stimuli that inject 0.8 nA currents for 1 ms, starting at 3 and 8 ms.
-cell.place('"stim_site"', arbor.iclamp(3, 1, current=0.5))
-cell.place('"stim_site"', arbor.iclamp(8, 1, current=1))
+cell.place('"stim_site"', arbor.iclamp(3, 1, current=2))
+cell.place('"stim_site"', arbor.iclamp(8, 1, current=4))
 # Detect spikes at the soma with a voltage threshold of -10 mV.
 cell.place('"root"', arbor.spike_detector(-10))
 
@@ -56,10 +54,10 @@ cell.compartments_on_segments()
 m = arbor.single_cell_model(cell)
 
 # Attach voltage probes that sample at 50 kHz.
-m.probe('voltage', where='root',  frequency=50000)
+m.probe('voltage', where='"root"',  frequency=50000)
 m.probe('voltage', where=loc(2,1),  frequency=50000)
-m.probe('voltage', where='stim_site',  frequency=50000)
-m.probe('voltage', where='axon_end', frequency=50000)
+m.probe('voltage', where='"stim_site"',  frequency=50000)
+m.probe('voltage', where='"axon_end"', frequency=50000)
 
 # Simulate the cell for 15 ms.
 tfinal=15
diff --git a/python/recipe.cpp b/python/recipe.cpp
index 45c5cfa3b2bf34a2dfca9e9da58dba816d0ad306..922f47e550c9d4b75ebd2e989013dccff2370297 100644
--- a/python/recipe.cpp
+++ b/python/recipe.cpp
@@ -29,7 +29,7 @@ arb::util::unique_any py_recipe_shim::get_cell_description(arb::cell_gid_type gi
                 "Python error already thrown");
 }
 
-arb::probe_info cable_loc_probe(std::string kind, arb::cell_member_type id, arb::mlocation loc) {
+arb::probe_info cable_loc_probe(std::string kind, arb::mlocation loc) {
     if (kind == "voltage") {
         return arb::cable_probe_membrane_voltage{loc};
     }
@@ -170,9 +170,9 @@ void register_recipe(pybind11::module& m) {
 
     // Probes
     m.def("cable_probe", &cable_loc_probe,
-        "Description of a probe at a location on a cable cell with id available for monitoring data of kind "\
+        "Description of a probe at a location available for monitoring data of kind "\
         "where kind is one of 'voltage' or 'ionic current density'.",
-        "kind"_a, "id"_a, "location"_a);
+        "kind"_a, "location"_a);
 
     pybind11::class_<arb::probe_info> probe(m, "probe");
     probe