diff --git a/tests/test_common_cells.hpp b/tests/test_common_cells.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c4641c879950fb6a5249cc25f38c3e6006df69ea --- /dev/null +++ b/tests/test_common_cells.hpp @@ -0,0 +1,112 @@ +#include <cell.hpp> +#include <parameter_list.hpp> + +namespace nest { +namespace mc { + +/* + * Create cell with just a soma: + * + * Soma: + * diameter: 18.8 µm + * mechanisms: membrane, HH + * memrane resistance: 123 Ω·cm + * + * Stimuli: + * soma centre, t=[10 ms, 110 ms), 0.1 nA + */ + +inline cell make_cell_soma_only() { + cell c; + + auto soma = c.add_soma(18.8/2.0); + soma->mechanism("membrane").set("r_L", 123); + soma->add_mechanism(hh_parameters()); + + c.add_stimulus({0,0.5}, {10., 100., 0.1}); + + return c; +} + +/* + * Create cell with a soma and unbranched dendrite: + * + * Soma: + * mechanisms: HH + * diameter: 12.6157 µm + * + * Dendrite: + * mechanisms: none + * diameter: 1 µm + * length: 200 µm + * membrane resistance: 100 Ω·cm + * compartments: 4 + * + * Stimulus: + * end of dendrite, t=[5 ms, 85 ms), 0.3 nA + */ + +inline cell make_cell_ball_and_stick() { + cell c; + + auto soma = c.add_soma(12.6157/2.0); + soma->add_mechanism(hh_parameters()); + + auto dendrite = c.add_cable(0, segmentKind::dendrite, 1.0/2, 1.0/2, 200.0); + dendrite->add_mechanism(pas_parameters()); + dendrite->mechanism("membrane").set("r_L", 100); + dendrite->set_compartments(4); + + c.add_stimulus({1,1}, {5., 80., 0.3}); + + return c; +} + +/* + * Create cell with a soma and three-segment dendrite with single branch point: + * + * O----====== + * + * Soma: + * mechanisms: HH + * diameter: 12.6157 µm + * + * Dendrites: + * mechanisms: membrane + * diameter: 1 µm + * length: 100 µm + * membrane resistance: 100 Ω·cm + * compartments: 4 + * + * Stimulus: + * end of first terminal branch, t=[5 ms, 85 ms), 0.45 nA + * end of second terminal branch, t=[40 ms, 50 ms), -0.2 nA + */ + +inline cell make_cell_ball_and_3sticks() { + cell c; + + auto soma = c.add_soma(12.6157/2.0); + soma->add_mechanism(hh_parameters()); + + // add dendrite of length 200 um and diameter 1 um with passive channel + c.add_cable(0, segmentKind::dendrite, 0.5, 0.5, 100); + c.add_cable(1, segmentKind::dendrite, 0.5, 0.5, 100); + c.add_cable(1, segmentKind::dendrite, 0.5, 0.5, 100); + + for (auto& seg: c.segments()) { + if (seg->is_dendrite()) { + seg->add_mechanism(pas_parameters()); + seg->mechanism("membrane").set("r_L", 100); + seg->set_compartments(4); + } + } + + c.add_stimulus({2,1}, {5., 80., 0.45}); + c.add_stimulus({3,1}, {40., 10.,-0.2}); + + return c; +} + +} // namespace mc +} // namespace nest diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 6b34d70125bb4d9792f8abb6b68daf411dfe78d8..1c286863c3e92a9c8f2a11eba8679f66094b3c72 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -18,6 +18,7 @@ set(TEST_SOURCES test_either.cpp test_event_queue.cpp test_fvm.cpp + test_fvm_multi.cpp test_cell_group.cpp test_lexcmp.cpp test_mask_stream.cpp diff --git a/tests/unit/test_cell_group.cpp b/tests/unit/test_cell_group.cpp index 3aed222029f4535a85d9713849cff184a3684531..99b46e564d66edc03643fba738dcd134ef9ac90c 100644 --- a/tests/unit/test_cell_group.cpp +++ b/tests/unit/test_cell_group.cpp @@ -4,24 +4,15 @@ #include <fvm_cell.hpp> #include <cell_group.hpp> +#include "../test_common_cells.hpp" + nest::mc::cell make_cell() { using namespace nest::mc; - nest::mc::cell cell; - - // Soma with diameter 12.6157 um and HH channel - auto soma = cell.add_soma(12.6157/2.0); - soma->add_mechanism(hh_parameters()); - - // add dendrite of length 200 um and diameter 1 um with passive channel - auto dendrite = cell.add_cable(0, segmentKind::dendrite, 0.5, 0.5, 200); - dendrite->add_mechanism(pas_parameters()); - dendrite->set_compartments(101); - - dendrite->mechanism("membrane").set("r_L", 100); + nest::mc::cell cell = make_cell_ball_and_stick(); cell.add_detector({0, 0}, 0); - cell.add_stimulus({1, 1}, {5., 80., 0.3}); + cell.segment(1)->set_compartments(101); return cell; } diff --git a/tests/unit/test_fvm.cpp b/tests/unit/test_fvm.cpp index ae7f0b5a3c15c01fc2c952c3ca84ff860bd10294..6d067182f972a0bc65147e8345f3043230d70d9a 100644 --- a/tests/unit/test_fvm.cpp +++ b/tests/unit/test_fvm.cpp @@ -7,40 +7,14 @@ #include <fvm_cell.hpp> #include <util/range.hpp> +#include "../test_common_cells.hpp" #include "../test_util.hpp" TEST(fvm, cable) { using namespace nest::mc; - nest::mc::cell cell; - - cell.add_soma(6e-4); // 6um in cm - - // 1um radius and 4mm long, all in cm - cell.add_cable(0, segmentKind::dendrite, 1e-4, 1e-4, 4e-1); - cell.add_cable(0, segmentKind::dendrite, 1e-4, 1e-4, 4e-1); - - //std::cout << cell.segment(1)->area() << " is the area\n"; - EXPECT_EQ(cell.model().tree.num_segments(), 3u); - - // add passive to all 3 segments in the cell - for(auto& seg :cell.segments()) { - seg->add_mechanism(pas_parameters()); - } - - cell.soma()->add_mechanism(hh_parameters()); - cell.segment(2)->add_mechanism(hh_parameters()); - - auto& soma_hh = cell.soma()->mechanism("hh"); - - soma_hh.set("gnabar", 0.12); - soma_hh.set("gkbar", 0.036); - soma_hh.set("gl", 0.0003); - soma_hh.set("el", -54.387); - - cell.segment(1)->set_compartments(4); - cell.segment(2)->set_compartments(4); + nest::mc::cell cell = make_cell_ball_and_3sticks(); using fvm_cell = fvm::fvm_cell<double, cell_lid_type>; @@ -53,7 +27,8 @@ TEST(fvm, cable) auto& J = fvcell.jacobian(); - EXPECT_EQ(cell.num_compartments(), 9u); + // 1 (soma) + 3 (dendritic segments) × 4 compartments + EXPECT_EQ(cell.num_compartments(), 13u); // assert that the matrix has one row for each compartment EXPECT_EQ(J.size(), cell.num_compartments()); @@ -69,21 +44,11 @@ TEST(fvm, init) { using namespace nest::mc; - nest::mc::cell cell; - - cell.add_soma(12.6157/2.0); - //auto& props = cell.soma()->properties; - - cell.add_cable(0, segmentKind::dendrite, 0.5, 0.5, 200); + nest::mc::cell cell = make_cell_ball_and_stick(); const auto m = cell.model(); EXPECT_EQ(m.tree.num_segments(), 2u); - // in this context (i.e. attached to a segment on a high-level cell) - // a mechanism is essentially a set of parameters - // - the only "state" is that used to define parameters - cell.soma()->add_mechanism(hh_parameters()); - auto& soma_hh = cell.soma()->mechanism("hh"); soma_hh.set("gnabar", 0.12); @@ -112,4 +77,3 @@ TEST(fvm, init) fvcell.setup_matrix(0.01); } - diff --git a/tests/unit/test_fvm_multi.cpp b/tests/unit/test_fvm_multi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbd4de99b91198128c6b3d0f0098f38f8440984d --- /dev/null +++ b/tests/unit/test_fvm_multi.cpp @@ -0,0 +1,142 @@ +#include <fstream> + +#include "gtest.h" + +#include <common_types.hpp> +#include <cell.hpp> +#include <fvm_multicell.hpp> +#include <util/range.hpp> + +#include "../test_util.hpp" +#include "../test_common_cells.hpp" + +TEST(fvm_multi, cable) +{ + using namespace nest::mc; + + nest::mc::cell cell=make_cell_ball_and_3sticks(); + + using fvm_cell = fvm::fvm_multicell<double, cell_lid_type>; + + std::vector<fvm_cell::target_handle> targets; + std::vector<fvm_cell::detector_handle> detectors; + std::vector<fvm_cell::probe_handle> probes; + + fvm_cell fvcell; + fvcell.initialize(util::singleton_view(cell), detectors, targets, probes); + + auto& J = fvcell.jacobian(); + + // 1 (soma) + 3 (dendritic segments) × 4 compartments + EXPECT_EQ(cell.num_compartments(), 13u); + + // assert that the matrix has one row for each compartment + EXPECT_EQ(J.size(), cell.num_compartments()); + + fvcell.setup_matrix(0.02); + + // assert that the number of cv areas is the same as the matrix size + // i.e. both should equal the number of compartments + EXPECT_EQ(fvcell.cv_areas().size(), J.size()); +} + +TEST(fvm_multi, init) +{ + using namespace nest::mc; + + nest::mc::cell cell = make_cell_ball_and_stick(); + + const auto m = cell.model(); + EXPECT_EQ(m.tree.num_segments(), 2u); + + auto& soma_hh = cell.soma()->mechanism("hh"); + + soma_hh.set("gnabar", 0.12); + soma_hh.set("gkbar", 0.036); + soma_hh.set("gl", 0.0003); + soma_hh.set("el", -54.3); + + // check that parameter values were set correctly + EXPECT_EQ(cell.soma()->mechanism("hh").get("gnabar").value, 0.12); + EXPECT_EQ(cell.soma()->mechanism("hh").get("gkbar").value, 0.036); + EXPECT_EQ(cell.soma()->mechanism("hh").get("gl").value, 0.0003); + EXPECT_EQ(cell.soma()->mechanism("hh").get("el").value, -54.3); + + cell.segment(1)->set_compartments(10); + + using fvm_cell = fvm::fvm_multicell<double, cell_lid_type>; + std::vector<fvm_cell::target_handle> targets; + std::vector<fvm_cell::detector_handle> detectors; + std::vector<fvm_cell::probe_handle> probes; + + fvm_cell fvcell; + fvcell.initialize(util::singleton_view(cell), detectors, targets, probes); + + auto& J = fvcell.jacobian(); + EXPECT_EQ(J.size(), 11u); + + fvcell.setup_matrix(0.01); +} + +TEST(fvm_multi, multi_init) +{ + using namespace nest::mc; + + nest::mc::cell cells[] = { + make_cell_ball_and_stick(), + make_cell_ball_and_3sticks() + }; + + EXPECT_EQ(cells[0].num_segments(), 2u); + EXPECT_EQ(cells[0].segment(1)->num_compartments(), 4u); + EXPECT_EQ(cells[1].num_segments(), 4u); + EXPECT_EQ(cells[1].segment(1)->num_compartments(), 4u); + EXPECT_EQ(cells[1].segment(2)->num_compartments(), 4u); + EXPECT_EQ(cells[1].segment(3)->num_compartments(), 4u); + + cells[0].add_synapse({1, 0.4}, parameter_list("expsyn")); + cells[0].add_synapse({1, 0.4}, parameter_list("expsyn")); + cells[1].add_synapse({2, 0.4}, parameter_list("exp2syn")); + cells[1].add_synapse({3, 0.4}, parameter_list("expsyn")); + + cells[1].add_detector({0, 0}, 3.3); + + using fvm_cell = fvm::fvm_multicell<double, cell_lid_type>; + std::vector<fvm_cell::target_handle> targets(4); + std::vector<fvm_cell::detector_handle> detectors(1); + std::vector<fvm_cell::probe_handle> probes; + + fvm_cell fvcell; + fvcell.initialize(cells, detectors, targets, probes); + + auto& J = fvcell.jacobian(); + EXPECT_EQ(J.size(), 5u+13u); + + // check indices in instantiated mechanisms + for (const auto& mech: fvcell.mechanisms()) { + if (mech->name()=="hh") { + // HH on somas of two cells, with group compartment indices + // 0 and 5. + ASSERT_EQ(mech->node_index().size(), 2u); + EXPECT_EQ(mech->node_index()[0], 0u); + EXPECT_EQ(mech->node_index()[1], 5u); + } + if (mech->name()=="expsyn") { + // Three expsyn synapses, two in second compartment + // of dendrite segment of first cell, one in second compartment + // of last segment of second cell. + ASSERT_EQ(mech->node_index().size(), 3u); + EXPECT_EQ(mech->node_index()[0], 2u); + EXPECT_EQ(mech->node_index()[1], 2u); + EXPECT_EQ(mech->node_index()[2], 15u); + } + if (mech->name()=="exp2syn") { + // One exp2syn synapse, in second compartment + // of penultimate segment of second cell. + ASSERT_EQ(mech->node_index().size(), 1u); + EXPECT_EQ(mech->node_index()[0], 11u); + } + } + + fvcell.setup_matrix(0.01); +} diff --git a/tests/validation/validate_ball_and_stick.cpp b/tests/validation/validate_ball_and_stick.cpp index aa5102eabfc6131fa0bc6d12893cf5c257339836..da34cfcff48d5bae6829edf81140b322c7a7c107 100644 --- a/tests/validation/validate_ball_and_stick.cpp +++ b/tests/validation/validate_ball_and_stick.cpp @@ -7,6 +7,8 @@ #include <util/range.hpp> #include "gtest.h" + +#include "../test_common_cells.hpp" #include "../test_util.hpp" #include "validation_data.hpp" @@ -16,20 +18,7 @@ TEST(ball_and_stick, neuron_baseline) using namespace nest::mc; using namespace nlohmann; - nest::mc::cell cell; - - // Soma with diameter 12.6157 um and HH channel - auto soma = cell.add_soma(12.6157/2.0); - soma->add_mechanism(hh_parameters()); - - // add dendrite of length 200 um and diameter 1 um with passive channel - auto dendrite = cell.add_cable(0, segmentKind::dendrite, 0.5, 0.5, 200); - dendrite->add_mechanism(pas_parameters()); - - dendrite->mechanism("membrane").set("r_L", 100); - - // add stimulus - cell.add_stimulus({1,1}, {5., 80., 0.3}); + nest::mc::cell cell = make_cell_ball_and_stick(); // load data from file auto cell_data = testing::g_validation_data.load("ball_and_stick.json"); @@ -89,10 +78,10 @@ TEST(ball_and_stick, neuron_baseline) std::vector<fvm_cell::probe_handle> probes(cell.probes().size()); std::vector<result> results; - for(auto run_index=0u; run_index<cell_data.size(); ++run_index) { + for (auto run_index=0u; run_index<cell_data.size(); ++run_index) { auto& run = cell_data[run_index]; int num_compartments = run["nseg"]; - dendrite->set_compartments(num_compartments); + cell.segment(1)->set_compartments(num_compartments); std::vector<std::vector<double>> v(3); // make the lowered finite volume cell @@ -164,26 +153,7 @@ TEST(ball_and_3stick, neuron_baseline) using namespace nest::mc; using namespace nlohmann; - nest::mc::cell cell; - - // Soma with diameter 12.6157 um and HH channel - auto soma = cell.add_soma(12.6157/2.0); - soma->add_mechanism(hh_parameters()); - - // add dendrite of length 200 um and diameter 1 um with passive channel - std::vector<cable_segment*> dendrites; - dendrites.push_back(cell.add_cable(0, segmentKind::dendrite, 0.5, 0.5, 100)); - dendrites.push_back(cell.add_cable(1, segmentKind::dendrite, 0.5, 0.5, 100)); - dendrites.push_back(cell.add_cable(1, segmentKind::dendrite, 0.5, 0.5, 100)); - - for(auto dend : dendrites) { - dend->add_mechanism(pas_parameters()); - dend->mechanism("membrane").set("r_L", 100); - } - - // add stimulus - cell.add_stimulus({2,1}, {5., 80., 0.45}); - cell.add_stimulus({3,1}, {40., 10.,-0.2}); + nest::mc::cell cell = make_cell_ball_and_3sticks(); // load data from file auto cell_data = testing::g_validation_data.load("ball_and_3stick.json"); @@ -247,8 +217,10 @@ TEST(ball_and_3stick, neuron_baseline) for(auto run_index=0u; run_index<cell_data.size(); ++run_index) { auto& run = cell_data[run_index]; int num_compartments = run["nseg"]; - for(auto dend : dendrites) { - dend->set_compartments(num_compartments); + for (auto& seg: cell.segments()) { + if (seg->is_dendrite()) { + seg->set_compartments(num_compartments); + } } std::vector<std::vector<double>> v(3); diff --git a/tests/validation/validate_soma.cpp b/tests/validation/validate_soma.cpp index b5b1517fcfdd1878c5386b89bf55cb2b4cec2d11..8952ba30c93d5c7121fa09c6092e281d39564eb4 100644 --- a/tests/validation/validate_soma.cpp +++ b/tests/validation/validate_soma.cpp @@ -7,7 +7,9 @@ #include <util/range.hpp> #include "gtest.h" + #include "../test_util.hpp" +#include "../test_common_cells.hpp" #include "validation_data.hpp" // compares results with those generated by nrn/soma.py @@ -17,15 +19,7 @@ TEST(soma, neuron_baseline) using namespace nest::mc; using namespace nlohmann; - nest::mc::cell cell; - - // Soma with diameter 18.8um and HH channel - auto soma = cell.add_soma(18.8/2.0); - soma->mechanism("membrane").set("r_L", 123); // no effect for single compartment cell - soma->add_mechanism(hh_parameters()); - - // add stimulus to the soma - cell.add_stimulus({0,0.5}, {10., 100., 0.1}); + nest::mc::cell cell = make_cell_soma_only(); // make the lowered finite volume cell using fvm_cell = fvm::fvm_cell<double, cell_local_size_type>; @@ -86,15 +80,7 @@ TEST(soma, convergence) { using namespace nest::mc; - nest::mc::cell cell; - - // Soma with diameter 18.8um and HH channel - auto soma = cell.add_soma(18.8/2.0); - soma->mechanism("membrane").set("r_L", 123); // no effect for single compartment cell - soma->add_mechanism(hh_parameters()); - - // add stimulus to the soma - cell.add_stimulus({0,0.5}, {10., 100., 0.1}); + nest::mc::cell cell = make_cell_soma_only(); // make the lowered finite volume cell using fvm_cell = fvm::fvm_cell<double, cell_local_size_type>;