diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp
index db52f29b0b1bb39517883a0069b720322cef48b5..f574615c67e2e0f7bb2d5b9e1bccf0383cd284b7 100644
--- a/arbor/fvm_layout.cpp
+++ b/arbor/fvm_layout.cpp
@@ -738,7 +738,7 @@ fvm_mechanism_data fvm_build_mechanism_data(
     using index_type = fvm_index_type;
     using value_type = fvm_value_type;
 
-    const mechanism_catalogue& catalogue = *gprop.catalogue;
+    const mechanism_catalogue& catalogue = gprop.catalogue;
     const auto& embedding = cell.embedding();
 
     const auto& global_dflt = gprop.default_parameters;
diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp
index e6f5143ba0b2968570bb5912098e234bbe046f49..a548e4af16e75201b66676f07798a6cc8c51472b 100644
--- a/arbor/fvm_lowered_cell_impl.hpp
+++ b/arbor/fvm_lowered_cell_impl.hpp
@@ -435,11 +435,11 @@ fvm_initialization_data fvm_lowered_cell_impl<Backend>::initialize(
     // (Throws cable_cell_error on failure.)
     check_global_properties(global_props);
 
-    const mechanism_catalogue* catalogue = global_props.catalogue;
+    const auto& catalogue = global_props.catalogue;
 
     // Mechanism instantiator helper.
     auto mech_instance = [&catalogue](const std::string& name) {
-        return catalogue->instance(backend::kind, name);
+        return catalogue.instance(backend::kind, name);
     };
 
     // Check for physically reasonable membrane volages?
diff --git a/arbor/include/arbor/cable_cell_param.hpp b/arbor/include/arbor/cable_cell_param.hpp
index 7d36e2233ec82dd326753a6aee952c59b11792fe..d1ad2bfa66ec931b53572d2bee91527a39b933c5 100644
--- a/arbor/include/arbor/cable_cell_param.hpp
+++ b/arbor/include/arbor/cable_cell_param.hpp
@@ -293,7 +293,7 @@ extern cable_cell_parameter_set neuron_parameter_defaults;
 // Global cable cell data.
 
 struct cable_cell_global_properties {
-    const mechanism_catalogue* catalogue = &global_default_catalogue();
+    mechanism_catalogue catalogue = global_default_catalogue();
 
     // If >0, check membrane voltage magnitude is less than limit
     // during integration.
diff --git a/arbor/include/arbor/mechcat.hpp b/arbor/include/arbor/mechcat.hpp
index 0db56c5596a4cd8c48241c683a7f6b8ee0ba76b2..224437049ffaffce172d7005ad43694f78747231 100644
--- a/arbor/include/arbor/mechcat.hpp
+++ b/arbor/include/arbor/mechcat.hpp
@@ -108,15 +108,12 @@ private:
     void register_impl(arb_backend_kind, const std::string&, mechanism_ptr);
 };
 
-
-// Reference to global default mechanism catalogue.
-
+// References to global mechanism catalogues.
 const mechanism_catalogue& global_default_catalogue();
 const mechanism_catalogue& global_allen_catalogue();
 const mechanism_catalogue& global_bbp_catalogue();
 
 // Load catalogue from disk.
-
 const mechanism_catalogue& load_catalogue(const std::string&);
 
 } // namespace arb
diff --git a/arbor/mechcat.cpp b/arbor/mechcat.cpp
index 3796b46aa45b0c1ab820f0dca3132e3b378da989..3dc187742e5c5a5c1e04ef63ef90a7e07528f573 100644
--- a/arbor/mechcat.cpp
+++ b/arbor/mechcat.cpp
@@ -117,6 +117,15 @@ struct catalogue_state {
         import(other, "");
     }
 
+    catalogue_state& operator=(const catalogue_state& other) {
+        *this = {};
+        import(other, "");
+        return *this;
+    }
+
+    catalogue_state& operator=(catalogue_state&&) = default;
+    catalogue_state(catalogue_state&& other) = default;
+
     void import(const catalogue_state& other, const std::string& prefix) {
         // Do all checks before adding anything, otherwise we might get inconsistent state.
         auto assert_undefined = [&](const std::string& key) {
@@ -508,8 +517,7 @@ struct catalogue_state {
 // Mechanism catalogue method implementations.
 
 mechanism_catalogue::mechanism_catalogue():
-    state_(new catalogue_state)
-{}
+    state_(new catalogue_state) {}
 
 std::vector<std::string> mechanism_catalogue::mechanism_names() const {
     return state_->mechanism_names();
diff --git a/doc/tutorial/single_cell_detailed.rst b/doc/tutorial/single_cell_detailed.rst
index a5db5411911a9b9a2e2392bcb5d346316e33816c..85e3f66130f858f30bab6289221c5d758975a40e 100644
--- a/doc/tutorial/single_cell_detailed.rst
+++ b/doc/tutorial/single_cell_detailed.rst
@@ -308,9 +308,9 @@ The global properties
 The global properties of a single cell model include:
 
 1. The **mechanism catalogue**: A mechanism catalogue is a collection of density and point
-   mechanisms. Arbor has 3 built in mechanism catalogues: default, allen and bbp. The mechanism
+   mechanisms. Arbor has 3 built-in mechanism catalogues: ``default``, ``allen`` and ``bbp``. The mechanism
    catalogue in the global properties of the model must include the catalogues of all the
-   mechanisms painted on the cell decor.
+   mechanisms painted on the cell decor. The default is to use the ``default_catalogue``.
 
 2. The default **parameters**: The initial membrane voltage; the initial temperature; the
    axial resistivity; the membrane capacitance; the ion parameters; and the discretisation
diff --git a/doc/tutorial/single_cell_detailed_recipe.rst b/doc/tutorial/single_cell_detailed_recipe.rst
index 73af662c1599c2df6ccb4dda972334ff8372aef3..f6f8863fb80af0dcc44d4e2378e6f34e3b30d889 100644
--- a/doc/tutorial/single_cell_detailed_recipe.rst
+++ b/doc/tutorial/single_cell_detailed_recipe.rst
@@ -59,7 +59,7 @@ examine the recipe in detail: how to create one, and why it is needed.
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 91-143
+   :lines: 91-139
 
 Let's go through the recipe point by point.
 
@@ -140,7 +140,7 @@ previous section:
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 145-147
+   :lines: 141-143
 
 The execution context
 *********************
@@ -157,7 +157,7 @@ the ideal settings can usually be inferred from the system, and Arbor can do tha
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 149-151
+   :lines: 145-146
 
 The domain decomposition
 ************************
@@ -176,7 +176,7 @@ we can use the load balancer, which does a good job distributing simple networks
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 153-155
+   :lines: 148-149
 
 The simulation
 **************
@@ -185,7 +185,7 @@ Finally we have the 3 components needed to create a :class:`arbor.simulation` ob
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 157-159
+   :lines: 151-152
 
 Before we run the simulation, however, we need to register what results we expect once execution is over.
 This was handled by the :class:`arbor.single_cell_model` object in the previous example.
@@ -195,7 +195,7 @@ to plot the voltage registered by the probe on the "custom_terminal" locset.
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 161-166
+   :lines: 154-158
 
 The lines handling probe sampling warrant a second look. First, we declared ``probe_id`` to be a
 :class:`arbor.cell_member`, with :class:`arbor.cell_member.gid` = 0 and :class:`arbor.cell_member.index` = 0.
@@ -213,7 +213,7 @@ We can now run the simulation we just instantiated for a duration of 100 ms with
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 168-170
+   :lines: 169-161
 
 The results
 ***********
@@ -225,13 +225,13 @@ We can print the times of the spikes:
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 172-177
+   :lines: 163-167
 
 The probe results, again, warrant some more explanation:
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 179-183
+   :lines: 169-173
 
 ``sim.samples()`` takes a ``handle`` of the probe we wish to examine. It returns a list
 of ``(data, meta)`` terms: ``data`` being the time and value series of the probed quantity; and
@@ -244,7 +244,7 @@ We plot the results using pandas and seaborn as we did in the previous example,
 
 .. literalinclude:: ../../python/example/single_cell_detailed_recipe.py
    :language: python
-   :lines: 185-188
+   :lines: 175-179
 
 The following plot is generated. Identical to the plot of the previous example.
 
diff --git a/doc/tutorial/single_cell_recipe.rst b/doc/tutorial/single_cell_recipe.rst
index bdc4b0dc739c1e8b389fb9ad600fbdc9975dec6f..33061c4b1f5b9e842a7983bc33d143e32d512475 100644
--- a/doc/tutorial/single_cell_recipe.rst
+++ b/doc/tutorial/single_cell_recipe.rst
@@ -40,7 +40,7 @@ It returns `0` by default and models without cells are quite boring!
 
 .. literalinclude:: ../../python/example/single_cell_recipe.py
    :language: python
-   :lines: 25-62
+   :lines: 25-60
 
 Step **(4)** describes the recipe that will reflect our single cell model.
 
@@ -85,7 +85,7 @@ leave the details of this subject for another tutorial.
 
 .. literalinclude:: ../../python/example/single_cell_recipe.py
    :language: python
-   :lines: 64-67
+   :lines: 62-65
 
 Step **(6)** sets up a default context and domains.
 
@@ -94,7 +94,7 @@ The simulation
 
 .. literalinclude:: ../../python/example/single_cell_recipe.py
    :language: python
-   :lines: 69-75
+   :lines: 67-73
 
 Step **(7)** instantiates the simulation and sets up the probe added in step 5. In the
 :class:`arbor.single_cell_model` version of this example, the probe frequency and
@@ -111,7 +111,7 @@ If we create the same analysis of the results we therefore expect the same resul
 
 .. literalinclude:: ../../python/example/single_cell_recipe.py
    :language: python
-   :lines: 77-94
+   :lines: 75-92
 
 Step **(8)** plots the measured potentials during the runtime of the simulation.
 Retrieving the sampled quantities is a little different, these have to be accessed
diff --git a/python/cells.cpp b/python/cells.cpp
index 90932f3811ec236983821283c29e7850e003c936..175ce8125fe9fc44b21f53f96069a42d56fa50d5 100644
--- a/python/cells.cpp
+++ b/python/cells.cpp
@@ -556,11 +556,10 @@ void register_cells(pybind11::module& m) {
             "Species concentrations and reversal potential can be overridden on\n"
             "specific regions using the paint interface, while the method for calculating\n"
             "reversal potential is global for all compartments in the cell, and can't be\n"
-            "overriden locally.")
-        .def("register", [](arb::cable_cell_global_properties& props, const arb::mechanism_catalogue& cat) {
-                props.catalogue = &cat;
-            },
-            "Register the pointer to the mechanism catalogue in the global properties")
+             "overriden locally.")
+        .def_readwrite("catalogue",
+                       &arb::cable_cell_global_properties::catalogue,
+                       "The mechanism catalogue.")
         .def("__str__", [](const arb::cable_cell_global_properties& p){return to_string(p);});
 
     m.def("neuron_cable_properties", []() {
diff --git a/python/example/dynamic-catalogue.py b/python/example/dynamic-catalogue.py
index 48248e72580a0b7abdb60969b16638962cdd798b..773eab0415d400a4d95d194dc105c706e84b0f99 100644
--- a/python/example/dynamic-catalogue.py
+++ b/python/example/dynamic-catalogue.py
@@ -12,8 +12,7 @@ class recipe(arb.recipe):
         self.tree = arb.segment_tree()
         self.tree.append(arb.mnpos, (0, 0, 0, 10), (1, 0, 0, 10), 1)
         self.props = arb.neuron_cable_properties()
-        self.cat = arb.load_catalogue(cat)
-        self.props.register(self.cat)
+        self.props.catalogue = arb.load_catalogue(cat)
         d = arb.decor()
         d.paint('(all)', 'dummy')
         d.set_property(Vm=0.0)
diff --git a/python/example/gap_junctions.py b/python/example/gap_junctions.py
index 6564f90fb13799a3275ee18c527cf9d8ef0e6b13..8985b3f3305741b3bb261ebb8b32ee2bebb3ad03 100644
--- a/python/example/gap_junctions.py
+++ b/python/example/gap_junctions.py
@@ -66,8 +66,6 @@ class chain_recipe(arbor.recipe):
         self.nchains = nchains
         self.ncells_per_chain = ncells_per_chain
         self.props = arbor.neuron_cable_properties()
-        self.cat = arbor.default_catalogue()
-        self.props.register(self.cat)
 
     def num_cells(self):
         return self.ncells_per_chain * self.nchains
@@ -161,4 +159,4 @@ for gid in range(ncells):
 
 df = pandas.concat(df_list,ignore_index=True)
 seaborn.relplot(data=df, kind="line", x="t/ms", y="U/mV",hue="Cell",ci=None)
-plt.show()
\ No newline at end of file
+plt.show()
diff --git a/python/example/network_ring.py b/python/example/network_ring.py
index 691c05adc988e6c4de9b8871cbc7411ee9dcc49f..6794d2381524090dbfb50bc6132559cefa7544d4 100755
--- a/python/example/network_ring.py
+++ b/python/example/network_ring.py
@@ -67,8 +67,6 @@ class ring_recipe (arbor.recipe):
         arbor.recipe.__init__(self)
         self.ncells = ncells
         self.props = arbor.neuron_cable_properties()
-        self.cat = arbor.default_catalogue()
-        self.props.register(self.cat)
 
     # (6) The num_cells method that returns the total number of cells in the model
     # must be implemented.
diff --git a/python/example/network_ring_mpi.py b/python/example/network_ring_mpi.py
index c2deb1986aac178ee6885739fa2c77c89978c3f7..a403d9484d0be44386c04fdabbbfbc7e6152e24c 100644
--- a/python/example/network_ring_mpi.py
+++ b/python/example/network_ring_mpi.py
@@ -69,8 +69,6 @@ class ring_recipe (arbor.recipe):
         arbor.recipe.__init__(self)
         self.ncells = ncells
         self.props = arbor.neuron_cable_properties()
-        self.cat = arbor.default_catalogue()
-        self.props.register(self.cat)
 
     # (6) The num_cells method that returns the total number of cells in the model
     # must be implemented.
diff --git a/python/example/single_cell_cable.py b/python/example/single_cell_cable.py
index f22d26f0b0d094f3c217296b3747786b72d4f9e5..1043dba3aaf0479fcf2256c0270c71d33a3791e5 100755
--- a/python/example/single_cell_cable.py
+++ b/python/example/single_cell_cable.py
@@ -50,8 +50,6 @@ class Cable(arbor.recipe):
         self.cv_policy_max_extent = cv_policy_max_extent
 
         self.the_props = arbor.neuron_cable_properties()
-        self.the_cat = arbor.default_catalogue()
-        self.the_props.register(self.the_cat)
 
     def num_cells(self):
         return 1
diff --git a/python/example/single_cell_detailed.py b/python/example/single_cell_detailed.py
index 0bd1d3f280e9baef53b8f1d3bdd2f103e1d55807..7f53efb0b64bb7e19911303069bec11e0822fe91 100755
--- a/python/example/single_cell_detailed.py
+++ b/python/example/single_cell_detailed.py
@@ -98,7 +98,7 @@ model.properties.set_ion('k',  int_con=54.4, ext_con=2.5, rev_pot=-77)
 # The function takes a second string parameter that can prefix
 # the name of the mechanisms to avoid collisions between catalogues
 # in this case we have no collisions so we use an empty prefix string.
-model.catalogue.extend(arbor.allen_catalogue(), "")
+model.properties.catalogue.extend(arbor.allen_catalogue(), "")
 
 # (7) Add probes.
 
diff --git a/python/example/single_cell_detailed_recipe.py b/python/example/single_cell_detailed_recipe.py
index ee0f2857101cf26e43fb090c06bb469864114bfd..000b6bb30add4927edfc3297539c228a8f323f60 100644
--- a/python/example/single_cell_detailed_recipe.py
+++ b/python/example/single_cell_detailed_recipe.py
@@ -99,16 +99,12 @@ class single_recipe (arbor.recipe):
         self.the_cell = cell
         self.the_probes = probes
 
-        self.the_cat = arbor.default_catalogue()
-        self.the_cat.extend(arbor.allen_catalogue(), "")
-
         self.the_props = arbor.cable_global_properties()
         self.the_props.set_property(Vm=-65, tempK=300, rL=35.4, cm=0.01)
         self.the_props.set_ion(ion='na', int_con=10,   ext_con=140, rev_pot=50, method='nernst/na')
         self.the_props.set_ion(ion='k',  int_con=54.4, ext_con=2.5, rev_pot=-77)
         self.the_props.set_ion(ion='ca', int_con=5e-5, ext_con=2, rev_pot=132.5)
-
-        self.the_props.register(self.the_cat)
+        self.the_props.catalogue.extend(arbor.allen_catalogue(), "")
 
     # (6.2) Override the num_cells method
     def num_cells(self):
@@ -147,30 +143,24 @@ class single_recipe (arbor.recipe):
 recipe = single_recipe(cell, [probe])
 
 # (4) Create an execution context
-
 context = arbor.context()
 
 # (5) Create a domain decomposition
-
 domains = arbor.partition_load_balance(recipe, context)
 
 # (6) Create a simulation
-
 sim = arbor.simulation(recipe, domains, context)
 
 # Instruct the simulation to record the spikes and sample the probe
-
 sim.record(arbor.spike_recording.all)
 
 probe_id = arbor.cell_member(0,0)
 handle = sim.sample(probe_id, arbor.regular_schedule(0.02))
 
 # (7) Run the simulation
-
 sim.run(tfinal=100, dt=0.025)
 
 # (8) Print or display the results
-
 spikes = sim.spikes()
 print(len(spikes), 'spikes recorded:')
 for s in spikes:
diff --git a/python/example/single_cell_extracellular_potentials.py b/python/example/single_cell_extracellular_potentials.py
index adf3d629b2b0c0ac945819b37810aa24b810c787..adf94ed3c8baa101ae4ad1198c1d3dbf66773c5e 100644
--- a/python/example/single_cell_extracellular_potentials.py
+++ b/python/example/single_cell_extracellular_potentials.py
@@ -33,8 +33,6 @@ class Recipe (arbor.recipe):
         self.cprobe_id = (0, 2)
 
         self.the_props = arbor.neuron_cable_properties()
-        self.the_cat = arbor.default_catalogue()
-        self.the_props.register(self.the_cat)
 
     def num_cells(self):
         return 1
diff --git a/python/example/single_cell_recipe.py b/python/example/single_cell_recipe.py
index 8ef071c8d8b282b40aa79c3475d15e00f3e9a76f..ade3cea46b16ad9f42e4c48aae003e5bd5fe4501 100644
--- a/python/example/single_cell_recipe.py
+++ b/python/example/single_cell_recipe.py
@@ -34,8 +34,6 @@ class single_recipe (arbor.recipe):
         self.the_cell = cell
         self.the_probes = probes
         self.the_props = arbor.neuron_cable_properties()
-        self.the_cat = arbor.default_catalogue()
-        self.the_props.register(self.the_cat)
 
     def num_cells(self):
         # (4.2) Override the num_cells method
diff --git a/python/example/single_cell_stdp.py b/python/example/single_cell_stdp.py
index 761a331e99a7dd2de7fb9d6ab45c6c814f0e9001..54f4846fac864453110a77592537c5ca894747e4 100755
--- a/python/example/single_cell_stdp.py
+++ b/python/example/single_cell_stdp.py
@@ -13,8 +13,6 @@ class single_recipe(arbor.recipe):
         self.n_pairs = n_pairs
 
         self.the_props = arbor.neuron_cable_properties()
-        self.the_cat = arbor.default_catalogue()
-        self.the_props.register(self.the_cat)
 
     def num_cells(self):
         return 1
diff --git a/python/example/two_cell_gap_junctions.py b/python/example/two_cell_gap_junctions.py
index 852f1af2b24799ee309a58c69c449b9247d825c9..844b2f90a0361d6cbc02ae8760b8d7bf0d812eb5 100755
--- a/python/example/two_cell_gap_junctions.py
+++ b/python/example/two_cell_gap_junctions.py
@@ -44,8 +44,6 @@ class TwoCellsWithGapJunction(arbor.recipe):
         self.cv_policy_max_extent = cv_policy_max_extent
 
         self.the_props = arbor.neuron_cable_properties()
-        self.the_cat = arbor.default_catalogue()
-        self.the_props.register(self.the_cat)
 
     def num_cells(self):
         return 2
diff --git a/python/single_cell_model.cpp b/python/single_cell_model.cpp
index e639067895dcc8617bea81ef0635e5ba97836c0d..5165aa02b1c51a794e570a499f3f36c7a6270c6b 100644
--- a/python/single_cell_model.cpp
+++ b/python/single_cell_model.cpp
@@ -139,13 +139,12 @@ class single_cell_model {
 
 public:
     arb::cable_cell_global_properties gprop;
-    arb::mechanism_catalogue cat;
 
     single_cell_model(arb::cable_cell c):
         cell_(std::move(c)), ctx_(arb::make_context())
     {
         gprop.default_parameters = arb::neuron_parameter_defaults;
-        cat = arb::global_default_catalogue();
+        gprop.catalogue = arb::global_default_catalogue();
     }
 
     // example use:
@@ -168,7 +167,6 @@ public:
     }
 
     void run(double tfinal, double dt) {
-        gprop.catalogue = &cat;
         single_cell_recipe rec(cell_, probes_, gprop);
 
         auto domdec = arb::partition_load_balance(rec, ctx_);
@@ -214,7 +212,6 @@ public:
 
 void register_single_cell(pybind11::module& m) {
     using namespace pybind11::literals;
-
     pybind11::class_<trace> tr(m, "trace", "Values and meta-data for a sample-trace on a single cell model.");
     tr
         .def_readonly("variable", &trace::variable, "Name of the variable being recorded.")
@@ -259,7 +256,6 @@ void register_single_cell(pybind11::module& m) {
                 return m.traces();},
             "Holds sample traces after a call to run().")
         .def_readwrite("properties", &single_cell_model::gprop, "Global properties.")
-        .def_readwrite("catalogue", &single_cell_model::cat, "Mechanism catalogue.")
         .def("__repr__", [](const single_cell_model&){return "<arbor.single_cell_model>";})
         .def("__str__",  [](const single_cell_model&){return "<arbor.single_cell_model>";});
 }
diff --git a/python/test/unit/test_cable_probes.py b/python/test/unit/test_cable_probes.py
index 6050e661f02bc94a11d630777a4821da42b9484c..c9f0d15a197e53e70db74f2c210b8ae54c370066 100644
--- a/python/test/unit/test_cable_probes.py
+++ b/python/test/unit/test_cable_probes.py
@@ -26,9 +26,8 @@ class cc_recipe(A.recipe):
 
         self.cell = A.cable_cell(st, A.label_dict(), dec)
 
-        self.cat = A.default_catalogue()
         self.props = A.neuron_cable_properties()
-        self.props.register(self.cat)
+        self.props.catalogue = A.default_catalogue()
 
     def num_cells(self):
         return 1
diff --git a/python/test/unit/test_catalogues.py b/python/test/unit/test_catalogues.py
index 550dda8a7a1284f258488d3144091b25068bd2b5..4e469186cbb6c48757e521ff3a8797a7d698a638 100644
--- a/python/test/unit/test_catalogues.py
+++ b/python/test/unit/test_catalogues.py
@@ -13,11 +13,11 @@ class recipe(arb.recipe):
         self.tree.append(arb.mnpos, (0, 0, 0, 10), (1, 0, 0, 10), 1)
         self.props = arb.neuron_cable_properties()
         try:
-            self.cat = arb.default_catalogue()
-            self.props.register(self.cat)
+            self.props.catalogue = arb.load_catalogue('dummy-catalogue.so')
         except:
             print("Catalogue not found. Are you running from build directory?")
             raise
+        self.props.catalogue = arb.default_catalogue()
 
         d = arb.decor()
         d.paint('(all)', arb.density('pas'))
diff --git a/python/test/unit_distributed/test_simulator.py b/python/test/unit_distributed/test_simulator.py
index 249d51492d0440a00d6d415d18d3846c906a1d02..5c23498f84b0b4bfed69714b43b2c7dcfb759b00 100644
--- a/python/test/unit_distributed/test_simulator.py
+++ b/python/test/unit_distributed/test_simulator.py
@@ -17,9 +17,8 @@ class lifN_recipe(A.recipe):
     def __init__(self, n_cell):
         A.recipe.__init__(self)
         self.n_cell = n_cell
-        self.cat = A.default_catalogue()
         self.props = A.neuron_cable_properties()
-        self.props.register(self.cat)
+
     def num_cells(self):
         return self.n_cell
 
diff --git a/test/simple_recipes.hpp b/test/simple_recipes.hpp
index 5ff33ff6b3b2a10a92e784e9c15e745a284c2f39..1bba3d1ce5dc9292affc71730d34332aa32eb388 100644
--- a/test/simple_recipes.hpp
+++ b/test/simple_recipes.hpp
@@ -21,10 +21,8 @@ namespace arb {
 
 class simple_recipe_base: public recipe {
 public:
-    simple_recipe_base():
-        catalogue_(global_default_catalogue())
-    {
-        cell_gprop_.catalogue = &catalogue_;
+    simple_recipe_base() {
+        cell_gprop_.catalogue = global_default_catalogue();
         cell_gprop_.default_parameters = neuron_parameter_defaults;
     }
 
@@ -47,7 +45,7 @@ public:
     }
 
     mechanism_catalogue& catalogue() {
-        return catalogue_;
+        return cell_gprop_.catalogue;
     }
 
     void add_ion(const std::string& ion_name, int charge, double init_iconc, double init_econc, double init_revpot) {
@@ -61,7 +59,6 @@ public:
 protected:
     std::unordered_map<cell_gid_type, std::vector<probe_info>> probes_;
     cable_cell_global_properties cell_gprop_;
-    mechanism_catalogue catalogue_;
 };
 
 // Convenience derived recipe class for wrapping n copies of a single
diff --git a/test/unit/test_fvm_layout.cpp b/test/unit/test_fvm_layout.cpp
index ac1294fb2c9b1d33f149693ecdf23b708f762efd..e7836e77e5787375b829cfaed90b159779e8b7b3 100644
--- a/test/unit/test_fvm_layout.cpp
+++ b/test/unit/test_fvm_layout.cpp
@@ -984,9 +984,8 @@ TEST(fvm_layout, gj_example_2) {
 
     // Check the GJ CV map
     cable_cell_global_properties gprop;
-    auto cat = make_unit_test_catalogue();
-    cat.import(arb::global_default_catalogue(), "");
-    gprop.catalogue = &cat;
+    gprop.catalogue = make_unit_test_catalogue();
+    gprop.catalogue.import(arb::global_default_catalogue(), "");
     gprop.default_parameters = neuron_parameter_defaults;
 
     auto cells = system.cells();
@@ -1466,8 +1465,7 @@ TEST(fvm_layout, valence_verify) {
 
     fvm_cv_discretization D = fvm_cv_discretize(cells, neuron_parameter_defaults);
 
-    mechanism_catalogue testcat = make_unit_test_catalogue();
-    gprop.catalogue = &testcat;
+    gprop.catalogue = make_unit_test_catalogue();
 
     // Missing the 'cl' ion:
     EXPECT_THROW(fvm_build_mechanism_data(gprop, cells, gids, gj_conns, D), cable_cell_error);
@@ -1531,9 +1529,8 @@ TEST(fvm_layout, ion_weights) {
         {0.}, {0., 1./2, 0.}, {1./4, 0., 0.}, {0., 0., 0., 0., 0.}, {3./4, 0.}
     };
 
-    mechanism_catalogue testcat = make_unit_test_catalogue();
     cable_cell_global_properties gprop;
-    gprop.catalogue = &testcat;
+    gprop.catalogue = make_unit_test_catalogue();
     gprop.default_parameters = neuron_parameter_defaults;
 
     fvm_value_type cai = gprop.default_parameters.ion_data["ca"].init_int_concentration.value();
@@ -1581,8 +1578,6 @@ TEST(fvm_layout, revpot) {
     //     * Reversal potential mechanisms are only extended where there exists another
     //       mechanism that reads them.
 
-    mechanism_catalogue testcat = make_unit_test_catalogue();
-
     soma_cell_builder builder(5);
     builder.add_branch(0, 100, 0.5, 0.5, 1, "dend");
     builder.add_branch(1, 200, 0.5, 0.5, 1, "dend");
@@ -1596,7 +1591,7 @@ TEST(fvm_layout, revpot) {
 
     cable_cell_global_properties gprop;
     gprop.default_parameters = neuron_parameter_defaults;
-    gprop.catalogue = &testcat;
+    gprop.catalogue = make_unit_test_catalogue();
 
     gprop.ion_species = {{"a", 1}, {"b", 2}, {"c", 3}};
     gprop.add_ion("a", 1, 10., 0, 0);
diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp
index 434a5db473d037f17d8bd6d50765c9fd476100c1..e1599b5d7f719f568a97c7cfdbdb4f691b28dd08 100644
--- a/test/unit/test_fvm_lowered.cpp
+++ b/test/unit/test_fvm_lowered.cpp
@@ -555,7 +555,6 @@ TEST(fvm_lowered, read_valence) {
         cell.decorations.paint("soma"_lab, density("cr_read_valence"));
         cable1d_recipe rec(cable_cell{cell});
         rec.catalogue() = make_unit_test_catalogue();
-        rec.catalogue() = make_unit_test_catalogue();
 
         rec.catalogue().derive("na_read_valence", "test_ca_read_valence", {}, {{"ca", "na"}});
         rec.catalogue().derive("cr_read_valence", "na_read_valence", {}, {{"na", "mn"}});
@@ -856,10 +855,7 @@ TEST(fvm_lowered, post_events_shared_state) {
                 ncell_(detectors_per_cell.size()),
                 ncv_(ncv),
                 detectors_per_cell_(detectors_per_cell),
-                synapse_(synapse),
-                cat_(make_unit_test_catalogue()) {
-            const auto default_cat = arb::global_default_catalogue();
-            cat_.import(default_cat, "");
+                synapse_(synapse) {
         }
 
         cell_size_type num_cells() const override {
@@ -890,7 +886,8 @@ TEST(fvm_lowered, post_events_shared_state) {
         std::any get_global_properties(arb::cell_kind) const override {
             arb::cable_cell_global_properties gprop;
             gprop.default_parameters = arb::neuron_parameter_defaults;
-            gprop.catalogue = &cat_;
+            gprop.catalogue = make_unit_test_catalogue();
+            gprop.catalogue.import(arb::global_default_catalogue(), "");
             return gprop;
         }
 
@@ -899,7 +896,6 @@ TEST(fvm_lowered, post_events_shared_state) {
         unsigned ncv_;
         std::vector<unsigned> detectors_per_cell_;
         arb::synapse synapse_;
-        mechanism_catalogue cat_;
     };
 
     std::vector<unsigned> gids = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
diff --git a/test/unit/test_mechinfo.cpp b/test/unit/test_mechinfo.cpp
index cd5b088530d75b8b73f0eb2f6e0b37366cfadc1c..3f1390aadd36205eb280d611427fc7aa3f646a56 100644
--- a/test/unit/test_mechinfo.cpp
+++ b/test/unit/test_mechinfo.cpp
@@ -44,12 +44,12 @@ TEST(mechanism_desc, setting) {
 
 TEST(mechanism_desc, linearity) {
     {
-        mechanism_catalogue cat = arb::global_default_catalogue();
+        auto cat = arb::global_default_catalogue();
         EXPECT_TRUE(cat["expsyn"].linear);
         EXPECT_TRUE(cat["exp2syn"].linear);
     }
     {
-        mechanism_catalogue cat = make_unit_test_catalogue();
+        auto cat = make_unit_test_catalogue();
         EXPECT_FALSE(cat["non_linear"].linear);
     }
 
diff --git a/test/unit/unit_test_catalogue.cpp b/test/unit/unit_test_catalogue.cpp
index bd04e05099bdff2e26474cd612dbf45736cdba9c..807ccabb7688127994a130da2b454236176e44f5 100644
--- a/test/unit/unit_test_catalogue.cpp
+++ b/test/unit/unit_test_catalogue.cpp
@@ -62,7 +62,7 @@ c.register_implementation(#x, std::make_unique<arb::mechanism>(make_testing_##x(
 using namespace arb;
 
 mechanism_catalogue make_unit_test_catalogue(const mechanism_catalogue& from) {
-    mechanism_catalogue cat(from);
+    mechanism_catalogue cat = from;
 
     ADD_MECH(cat, gj0)
     ADD_MECH(cat, gj1)