diff --git a/arbor/backends/gpu/mechanism.cpp b/arbor/backends/gpu/mechanism.cpp index 8d7c1aff253c63da3f2bbc7787207d3463e84563..81d5b29d845b54ce9c9f774c72e92ceb88ec0241 100644 --- a/arbor/backends/gpu/mechanism.cpp +++ b/arbor/backends/gpu/mechanism.cpp @@ -93,6 +93,7 @@ void mechanism::instantiate(unsigned id, pp->vec_g_ = shared.conductivity.data(); pp->temperature_degC_ = shared.temperature_degC.data(); + pp->diam_um_ = shared.diam_um.data(); auto ion_state_tbl = ion_state_table(); num_ions_ = ion_state_tbl.size(); diff --git a/arbor/backends/gpu/mechanism_ppack_base.hpp b/arbor/backends/gpu/mechanism_ppack_base.hpp index 3232ef4d77f0f80cd8740e648efb532fa2a23230..0727a82957a40adc04d35c47111a5a9a4a4949ef 100644 --- a/arbor/backends/gpu/mechanism_ppack_base.hpp +++ b/arbor/backends/gpu/mechanism_ppack_base.hpp @@ -38,6 +38,7 @@ struct mechanism_ppack_base { value_type* vec_i_; value_type* vec_g_; const value_type* temperature_degC_; + const value_type* diam_um_; const index_type* node_index_; const index_type* multiplicity_; diff --git a/arbor/backends/gpu/shared_state.cpp b/arbor/backends/gpu/shared_state.cpp index e15692beb7962f6f06a534cbc6ad26bbcf03d1bb..704ea7f5b89252e8b7d05445dba1ee4bca2786a6 100644 --- a/arbor/backends/gpu/shared_state.cpp +++ b/arbor/backends/gpu/shared_state.cpp @@ -94,6 +94,7 @@ shared_state::shared_state( const std::vector<fvm_gap_junction>& gj_vec, const std::vector<fvm_value_type>& init_membrane_potential, const std::vector<fvm_value_type>& temperature_K, + const std::vector<fvm_value_type>& diam, unsigned // alignment parameter ignored. ): n_intdom(n_intdom), @@ -110,6 +111,7 @@ shared_state::shared_state( conductivity(n_cv), init_voltage(make_const_view(init_membrane_potential)), temperature_degC(make_const_view(temperature_K)), + diam_um(make_const_view(diam)), deliverable_events(n_intdom) { add_scalar(temperature_degC.size(), temperature_degC.data(), -273.15); @@ -188,6 +190,7 @@ std::ostream& operator<<(std::ostream& o, shared_state& s) { o << " voltage " << s.voltage << "\n"; o << " init_voltage " << s.init_voltage << "\n"; o << " temperature " << s.temperature_degC << "\n"; + o << " diameter " << s.diam_um << "\n"; o << " current " << s.current_density << "\n"; o << " conductivity " << s.conductivity << "\n"; for (auto& ki: s.ion_data) { diff --git a/arbor/backends/gpu/shared_state.hpp b/arbor/backends/gpu/shared_state.hpp index a2d99456a8bdbc007899f4027384735c0802cb31..759ed1ca5d1bef2d55de3266707b3bc6088b7f0b 100644 --- a/arbor/backends/gpu/shared_state.hpp +++ b/arbor/backends/gpu/shared_state.hpp @@ -76,6 +76,7 @@ struct shared_state { array init_voltage; // Maps CV index to initial membrane voltage [mV]. array temperature_degC; // Maps CV to local temperature (read only) [°C]. + array diam_um; // Maps CV to local diameter (read only) [µm]. std::unordered_map<std::string, ion_state> ion_data; @@ -89,6 +90,7 @@ struct shared_state { const std::vector<fvm_gap_junction>& gj_vec, const std::vector<fvm_value_type>& init_membrane_potential, const std::vector<fvm_value_type>& temperature_K, + const std::vector<fvm_value_type>& diam, unsigned align ); diff --git a/arbor/backends/multicore/mechanism.cpp b/arbor/backends/multicore/mechanism.cpp index 5c62c1c3df4dba1ef6f05b80b7cbfb9ae083ff0f..22b9e3c521258b62fdc69656d79974bb313a0112 100644 --- a/arbor/backends/multicore/mechanism.cpp +++ b/arbor/backends/multicore/mechanism.cpp @@ -94,6 +94,7 @@ void mechanism::instantiate(unsigned id, backend::shared_state& shared, const me vec_g_ = shared.conductivity.data(); temperature_degC_ = shared.temperature_degC.data(); + diam_um_ = shared.diam_um.data(); auto ion_state_tbl = ion_state_table(); n_ion_ = ion_state_tbl.size(); diff --git a/arbor/backends/multicore/mechanism.hpp b/arbor/backends/multicore/mechanism.hpp index bcd5c46e808a5a79d937a48a50c7a783e53a8d37..bf7970b01e85841a056b40e9fda181fc8ea486b2 100644 --- a/arbor/backends/multicore/mechanism.hpp +++ b/arbor/backends/multicore/mechanism.hpp @@ -79,7 +79,8 @@ protected: const value_type* vec_v_; // CV to cell membrane voltage. value_type* vec_i_; // CV to cell membrane current density. value_type* vec_g_; // CV to cell membrane conductivity. - const value_type* temperature_degC_; // Pointer to global temperature scalar. + const value_type* temperature_degC_; // CV to temperature. + const value_type* diam_um_; // CV to diameter. deliverable_event_stream* event_stream_ptr_; // Per-mechanism index and weight data, excepting ion indices. diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp index 84986e53d14c33cc6590ef71746213c2c2b78bce..d4f98051d8a83c0cfcb2267d13a40ef15634c9bc 100644 --- a/arbor/backends/multicore/shared_state.cpp +++ b/arbor/backends/multicore/shared_state.cpp @@ -94,6 +94,7 @@ shared_state::shared_state( const std::vector<fvm_gap_junction>& gj_vec, const std::vector<fvm_value_type>& init_membrane_potential, const std::vector<fvm_value_type>& temperature_K, + const std::vector<fvm_value_type>& diam, unsigned align ): alignment(min_alignment(align)), @@ -112,6 +113,7 @@ shared_state::shared_state( conductivity(n_cv, pad(alignment)), init_voltage(init_membrane_potential.begin(), init_membrane_potential.end(), pad(alignment)), temperature_degC(n_cv, pad(alignment)), + diam_um(diam.begin(), diam.end(), pad(alignment)), deliverable_events(n_intdom) { // For indices in the padded tail of cv_to_intdom, set index to last valid intdom index. @@ -242,6 +244,7 @@ std::ostream& operator<<(std::ostream& out, const shared_state& s) { out << "voltage " << csv(s.voltage) << "\n"; out << "init_voltage " << csv(s.init_voltage) << "\n"; out << "temperature " << csv(s.temperature_degC) << "\n"; + out << "diameter " << csv(s.diam_um) << "\n"; out << "current " << csv(s.current_density) << "\n"; out << "conductivity " << csv(s.conductivity) << "\n"; for (const auto& ki: s.ion_data) { diff --git a/arbor/backends/multicore/shared_state.hpp b/arbor/backends/multicore/shared_state.hpp index 3a16c03a8ddaec0b8cae780080c1d5b3146eceac..eb9d3f3086f8b5d80c6992530c392daae3a986ae 100644 --- a/arbor/backends/multicore/shared_state.hpp +++ b/arbor/backends/multicore/shared_state.hpp @@ -94,6 +94,7 @@ struct shared_state { array init_voltage; // Maps CV index to initial membrane voltage [mV]. array temperature_degC; // Maps CV to local temperature (read only) [°C]. + array diam_um; // Maps CV to local diameter (read only) [µm]. std::unordered_map<std::string, ion_state> ion_data; @@ -107,6 +108,7 @@ struct shared_state { const std::vector<fvm_gap_junction>& gj_vec, const std::vector<fvm_value_type>& init_membrane_potential, const std::vector<fvm_value_type>& temperature_K, + const std::vector<fvm_value_type>& diam, unsigned align ); diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp index 26fb49664564231e0b6ccf0b6ff1862392f4d922..fd293f8434685cef4e49a256a44edf4bdfa52f36 100644 --- a/arbor/fvm_layout.cpp +++ b/arbor/fvm_layout.cpp @@ -233,6 +233,7 @@ fvm_discretization fvm_discretize(const std::vector<cable_cell>& cells, const ca D.cv_capacitance.assign(D.ncv, 0.); D.init_membrane_potential.assign(D.ncv, 0.); D.temperature_K.assign(D.ncv, 0.); + D.diam_um.assign(D.ncv, 0.); D.parent_cv.assign(D.ncv, index_type(-1)); D.cv_to_cell.resize(D.ncv); for (auto i: make_span(0, D.ncell)) { @@ -365,6 +366,7 @@ fvm_discretization fvm_discretize(const std::vector<cable_cell>& cells, const ca auto al = div.left.area; // [µm²] auto ar = div.right.area; // [µm²] + auto dr = div.right.radii.second*2; // [µm] D.cv_area[j] += al; // [µm²] D.cv_capacitance[j] += al*cm; // [pF] @@ -375,6 +377,7 @@ fvm_discretization fvm_discretize(const std::vector<cable_cell>& cells, const ca D.cv_capacitance[i] += ar*cm; // [pF] D.init_membrane_potential[i] += ar*init_vm; // [mV·µm²] D.temperature_K[i] += ar*temp; // [K·µm²] + D.diam_um[i] = dr; // [µm] } } @@ -386,6 +389,7 @@ fvm_discretization fvm_discretize(const std::vector<cable_cell>& cells, const ca D.cv_capacitance[soma_cv] = soma_area*soma_cm; // [pF] D.init_membrane_potential[soma_cv] = soma_area*soma_init_vm; // [mV·µm²] D.temperature_K[soma_cv] = soma_area*soma_temp; // [K·µm²] + D.diam_um[soma_cv] = soma->radius()*2; // [µm] } // Rescale CV init_vm and temperature values to get area-weighted means. diff --git a/arbor/fvm_layout.hpp b/arbor/fvm_layout.hpp index 879be39b806e9e2cf455655eb988d1087a0786c6..213fdcafb0a1e63d5a39bba8b0e278a972087d6e 100644 --- a/arbor/fvm_layout.hpp +++ b/arbor/fvm_layout.hpp @@ -68,6 +68,7 @@ struct fvm_discretization { std::vector<value_type> cv_capacitance; // [pF] std::vector<value_type> init_membrane_potential; // [mV] std::vector<value_type> temperature_K; // [K] + std::vector<value_type> diam_um; // [µm] std::vector<segment_info> segments; diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp index b016793e68aa2c559161d1170a5fd7f2c468e0df..429104e81fbae687c9da24b7c35bf818e6dcd20d 100644 --- a/arbor/fvm_lowered_cell_impl.hpp +++ b/arbor/fvm_lowered_cell_impl.hpp @@ -413,7 +413,7 @@ void fvm_lowered_cell_impl<B>::initialize( [&](const std::string& name) { return mech_instance(name).mech->data_alignment(); })); state_ = std::make_unique<shared_state>( - num_intdoms, cv_to_intdom, gj_vector, D.init_membrane_potential, D.temperature_K, + num_intdoms, cv_to_intdom, gj_vector, D.init_membrane_potential, D.temperature_K, D.diam_um, data_alignment? data_alignment: 1u); // Instantiate mechanisms and ions. diff --git a/modcc/identifier.hpp b/modcc/identifier.hpp index d1c900c978606bc7f45434df4f6cfa6f3c66a011..6cf466361032b5eaad20d8099321fd65b4d169fb 100644 --- a/modcc/identifier.hpp +++ b/modcc/identifier.hpp @@ -52,6 +52,7 @@ enum class sourceKind { ion_econc, ion_valence, temperature, + diameter, no_source }; diff --git a/modcc/module.cpp b/modcc/module.cpp index 30ada9a2e38e4c45f80b9abc838c2be344bfee80..0e5c4de5eda7f42df867146f96dee8e86fb6bacb 100644 --- a/modcc/module.cpp +++ b/modcc/module.cpp @@ -562,6 +562,9 @@ void Module::add_variables_to_symbols() { if (id.name() == "celsius") { create_indexed_variable("celsius", sourceKind::temperature, accessKind::read, "", Location()); } + else if (id.name() == "diam") { + create_indexed_variable("diam", sourceKind::diameter, accessKind::read, "", Location()); + } else { // Parameters are scalar by default, but may later be changed to range. auto& sym = create_variable(id.token, @@ -574,13 +577,19 @@ void Module::add_variables_to_symbols() { } } - // Remove `celsius` from the parameter block, as it is not a true parameter anymore. + // Remove `celsius` and `diam` from the parameter block, as they are not true parameters anymore. parameter_block_.parameters.erase( std::remove_if(parameter_block_.begin(), parameter_block_.end(), [](const Id& id) { return id.name() == "celsius"; }), parameter_block_.end() ); + parameter_block_.parameters.erase( + std::remove_if(parameter_block_.begin(), parameter_block_.end(), + [](const Id& id) { return id.name() == "diam"; }), + parameter_block_.end() + ); + // Add 'assigned' variables, ignoring built-in voltage variable "v". for (const Id& id: assigned_block_) { if (id.name() == "v") { diff --git a/modcc/printer/printerutil.cpp b/modcc/printer/printerutil.cpp index 4febdb01894843ece1cb01c16c723e1e720a09c7..0568e67d64e9b2d08213094905c0d90ce9f8d6af 100644 --- a/modcc/printer/printerutil.cpp +++ b/modcc/printer/printerutil.cpp @@ -185,7 +185,10 @@ indexed_variable_info decode_indexed_variable(IndexedVariable* sym) { break; case sourceKind::temperature: v.data_var = "temperature_degC_"; - v.index_var = ""; // scalar global + v.readonly = true; + break; + case sourceKind::diameter: + v.data_var = "diam_um_"; v.readonly = true; break; default: diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 910764d41e8830a88b1a3b6792dee921f518e82e..29e8591e08876e5c1c946e17f46e89d546a53ad5 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -2,6 +2,7 @@ set(test_mechanisms celsius_test + diam_test test_linear_state test_linear_init test_linear_init_shuffle @@ -102,9 +103,9 @@ set(unit_sources test_matrix.cpp test_mc_cell_group.cpp test_mechanisms.cpp + test_mech_temp_diam.cpp test_mechcat.cpp test_mechinfo.cpp - test_mech_temperature.cpp test_merge_events.cpp test_morphology.cpp test_multi_event_stream.cpp diff --git a/test/unit/mod/diam_test.mod b/test/unit/mod/diam_test.mod new file mode 100644 index 0000000000000000000000000000000000000000..c00b98057d342870da659301a8c12f9ae2d91b3b --- /dev/null +++ b/test/unit/mod/diam_test.mod @@ -0,0 +1,27 @@ +NEURON { + SUFFIX diam_test +} + +PARAMETER { + diam +} + +STATE { + d +} + +ASSIGNED { +} + +BREAKPOINT { + SOLVE states +} + +DERIVATIVE states { + d = diam +} + +INITIAL { + d = 0 +} + diff --git a/test/unit/test_fvm_layout.cpp b/test/unit/test_fvm_layout.cpp index 6ad308015644738ab31c73b50fdd30897cc85dc5..eb16024e46ce3575d087bdbac2b8dffffff6e56d 100644 --- a/test/unit/test_fvm_layout.cpp +++ b/test/unit/test_fvm_layout.cpp @@ -142,15 +142,15 @@ TEST(fvm_layout, topology) { // // Cell 0: // - // CV: | 0 | 1 | 2 | 3 | 4| - // [soma (0)][ segment (1) ] + // CV: | 0 ][1| 2 | 3 | 4 |5| + // [soma (0)][ segment (1) ] // // Cell 1: // - // CV: | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13| - // [soma (2)][ segment (3) ][ segment (4) ] - // [ segment (5) ] - // | 14 | 15 | 16 | 17| + // CV: | 6 ][7| 8 | 9 | 10| 11 | 12 | 13 | 14 | 15| + // [soma (2)][ segment (3) ][ segment (4) ] + // [ segment (5) ] + // | 16 | 17 | 18 | 19| EXPECT_EQ(2u, D.ncell); EXPECT_EQ(20u, D.ncv); @@ -213,7 +213,7 @@ TEST(fvm_layout, topology) { } } -TEST(fvm_layout, area) { +TEST(fvm_layout, diam_and_area) { std::vector<cable_cell> cells = two_cell_system(); check_two_cell_system(cells); @@ -222,6 +222,28 @@ TEST(fvm_layout, area) { // Note: stick models have constant diameter segments. // Refer to comment above for CV vs. segment layout. + EXPECT_FLOAT_EQ(12.6157, D.diam_um[0]); + EXPECT_FLOAT_EQ(1.0 , D.diam_um[1]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[2]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[3]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[4]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[5]); + + EXPECT_FLOAT_EQ(14.0, D.diam_um[6]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[7]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[8]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[9]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[10]); + EXPECT_FLOAT_EQ(1.0, D.diam_um[11]); + EXPECT_FLOAT_EQ(0.8, D.diam_um[12]); + EXPECT_FLOAT_EQ(0.8, D.diam_um[13]); + EXPECT_FLOAT_EQ(0.8, D.diam_um[14]); + EXPECT_FLOAT_EQ(0.8, D.diam_um[15]); + EXPECT_FLOAT_EQ(0.7, D.diam_um[16]); + EXPECT_FLOAT_EQ(0.7, D.diam_um[17]); + EXPECT_FLOAT_EQ(0.7, D.diam_um[18]); + EXPECT_FLOAT_EQ(0.7, D.diam_um[19]); + std::vector<double> A; for (auto ci: make_span(D.ncell)) { for (auto si: make_span(cells[ci].num_segments())) { @@ -238,7 +260,7 @@ TEST(fvm_layout, area) { EXPECT_FLOAT_EQ(A[1]/(2*n), D.cv_area[5]); EXPECT_FLOAT_EQ(A[2], D.cv_area[6]); - EXPECT_FLOAT_EQ(A[3]/(2*n), D.cv_area[7]); + EXPECT_FLOAT_EQ(A[3]/(2*n), D.cv_area[7]); EXPECT_FLOAT_EQ(A[3]/n, D.cv_area[8]); EXPECT_FLOAT_EQ(A[3]/n, D.cv_area[9]); EXPECT_FLOAT_EQ(A[3]/n, D.cv_area[10]); diff --git a/test/unit/test_kinetic_linear.cpp b/test/unit/test_kinetic_linear.cpp index fbbe0aad2ef8c23f1d1c95123aa34f9dfdd5e8b0..e4bd84035be2b504be96dc69468edef99b389218 100644 --- a/test/unit/test_kinetic_linear.cpp +++ b/test/unit/test_kinetic_linear.cpp @@ -43,10 +43,11 @@ void run_test(std::string mech_name, auto& test = instance.mech; std::vector<fvm_value_type> temp(ncv, 300.); + std::vector<fvm_value_type> diam(ncv, 1.); std::vector<fvm_value_type> vinit(ncv, -65); auto shared_state = std::make_unique<typename backend::shared_state>( - ncell, cv_to_intdom, gj, vinit, temp, test->data_alignment()); + ncell, cv_to_intdom, gj, vinit, temp, diam, test->data_alignment()); mechanism_layout layout; mechanism_overrides overrides; diff --git a/test/unit/test_mech_temperature.cpp b/test/unit/test_mech_temp_diam.cpp similarity index 53% rename from test/unit/test_mech_temperature.cpp rename to test/unit/test_mech_temp_diam.cpp index 1a616c76e6699bb4769ca224924a1ad79119acda..fbda2292660c7f75b21f787e69a5b267ed0b8648 100644 --- a/test/unit/test_mech_temperature.cpp +++ b/test/unit/test_mech_temp_diam.cpp @@ -32,10 +32,11 @@ void run_celsius_test() { double temperature_C = temperature_K-273.15; std::vector<fvm_value_type> temp(ncv, temperature_K); + std::vector<fvm_value_type> diam(ncv, 1.); std::vector<fvm_value_type> vinit(ncv, -65); auto shared_state = std::make_unique<typename backend::shared_state>( - ncell, cv_to_intdom, gj, vinit, temp, celsius_test->data_alignment()); + ncell, cv_to_intdom, gj, vinit, temp, diam, celsius_test->data_alignment()); mechanism_layout layout; mechanism_overrides overrides; @@ -63,12 +64,64 @@ void run_celsius_test() { EXPECT_EQ(expected_c_values, mechanism_field(celsius_test.get(), "c")); } +template <typename backend> +void run_diam_test() { + auto cat = make_unit_test_catalogue(); + + // one cell, three CVs: + + fvm_size_type ncell = 1; + fvm_size_type ncv = 3; + std::vector<fvm_index_type> cv_to_intdom(ncv, 0); + + std::vector<fvm_gap_junction> gj = {}; + auto instance = cat.instance<backend>("diam_test"); + auto& celsius_test = instance.mech; + + std::vector<fvm_value_type> temp(ncv, 300.); + std::vector<fvm_value_type> vinit(ncv, -65); + std::vector<fvm_value_type> diam(ncv); + + mechanism_layout layout; + mechanism_overrides overrides; + + layout.weight.assign(ncv, 1.); + + for (fvm_size_type i = 0; i < ncv; ++i) { + diam[i] = i*2 + 0.1; + layout.cv.push_back(i); + } + + auto shared_state = std::make_unique<typename backend::shared_state>( + ncell, cv_to_intdom, gj, vinit, temp, diam, celsius_test->data_alignment()); + + + celsius_test->instantiate(0, *shared_state, overrides, layout); + shared_state->reset(); + + // expect 0 value in state 'd' after init: + + celsius_test->initialize(); + std::vector<fvm_value_type> expected_d_values(ncv, 0.); + + EXPECT_EQ(expected_d_values, mechanism_field(celsius_test.get(), "d")); + + // expect original diam values in state 'd' after state update: + + celsius_test->nrn_state(); + expected_d_values = diam; + + EXPECT_EQ(expected_d_values, mechanism_field(celsius_test.get(), "d")); +} + TEST(mech_temperature, celsius) { run_celsius_test<multicore::backend>(); + run_diam_test<multicore::backend>(); } #ifdef ARB_GPU_ENABLED TEST(mech_temperature_gpu, celsius) { run_celsius_test<gpu::backend>(); + run_diam_test<gpu::backend>(); } #endif diff --git a/test/unit/test_synapses.cpp b/test/unit/test_synapses.cpp index 03695d9345eebe3cbc8cf267d0ee50810d6a7d0d..5101732d2edd991386c2ac9784191c99228d407f 100644 --- a/test/unit/test_synapses.cpp +++ b/test/unit/test_synapses.cpp @@ -98,6 +98,7 @@ TEST(synapses, syn_basic_state) { {}, std::vector<value_type>(num_comp, -65), std::vector<value_type>(num_comp, temp_K), + std::vector<value_type>(num_comp, 1.), align); state.reset(); diff --git a/test/unit/unit_test_catalogue.cpp b/test/unit/unit_test_catalogue.cpp index 7f803dc21b6da6e3c62cf294e70822822272a0c2..f86219ac8efb48e8ba71973c0eea89e2a2396d33 100644 --- a/test/unit/unit_test_catalogue.cpp +++ b/test/unit/unit_test_catalogue.cpp @@ -8,6 +8,7 @@ #include "unit_test_catalogue.hpp" #include "mechanisms/celsius_test.hpp" +#include "mechanisms/diam_test.hpp" #include "mechanisms/test0_kin_diff.hpp" #include "mechanisms/test_linear_state.hpp" #include "mechanisms/test_linear_init.hpp" @@ -47,6 +48,7 @@ mechanism_catalogue make_unit_test_catalogue() { mechanism_catalogue cat; ADD_MECH(cat, celsius_test) + ADD_MECH(cat, diam_test) ADD_MECH(cat, test_linear_state) ADD_MECH(cat, test_linear_init) ADD_MECH(cat, test_linear_init_shuffle)