def test_copy_group(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc1 = h.TestCell();')

            # Load cell into first group
            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Create 2nd cell
            ncn.client.run_command('tc2 = h.TestCell();')

            # Add second group, and shift 2nd cell up by 5 um
            bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_add();"
                "bpy.ops.blenderneuron.import_groups();"
                "bpy.data.objects['TestCell[1].soma'].location[2] += 5;")

            # Test group copying logic
            group_int_granularity = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].interaction_granularity = 'Section';"
                "bpy.context.scene.BlenderNEURON.groups_index = 1;"
                "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[1];"
                "group.copy_from_group = 'Group.000';"
                "bpy.ops.blenderneuron.copy_from_group();"
                "return_value = group.interaction_granularity;")

            self.assertEqual(group_int_granularity, 'Section')

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
    def test_ugly_render_curves(self):

        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
             Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            # Load TestCell.hoc
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc = h.TestCell();')

            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Non-smooth curves
            bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].interaction_granularity = 'Cell';"
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].smooth_sections = False;"
                "bpy.ops.blenderneuron.import_groups();")

            # Non-smooth curves
            bevel = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].interaction_granularity = 'Cell';"
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].as_lines = True;"
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = bpy.data.curves['Group.000_bezier.001'].bevel_depth"
            )

            # Should have no bevel
            self.assertEqual(bevel, 0)
    def test_remove_cell_from_nrn(self):

        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
                Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc1 = h.TestCell();')

            # Load cell into first group and show it
            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();"
                "bpy.ops.blenderneuron.import_groups();")

            # Create 2nd cell
            ncn.client.run_command('tc2 = h.TestCell();')

            # Delete the first cell from NRN
            ncn.client.run_command('tc1 = None;' 'del tc1;')

            # Add second group, check the number of ui cells
            ui_cell_count = bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_add();"
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = len(bpy.context.scene.BlenderNEURON.groups[0].root_entries);"
            )

            self.assertEqual(ui_cell_count, 1)

            # Get the name of the visible cell of the second group
            ui_cell_name = bcn.client.run_command(
                "return_value = bpy.context.scene.BlenderNEURON.groups[1].root_entries[0].name;"
            )

            self.assertEqual(ui_cell_name, "TestCell[1].soma")
예제 #4
0
    def test_glare(self):
        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
                Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            bcn.client.run_command('bpy.ops.blenderneuron.add_neon_effect();')

            # Adding 2nd time should not fail either
            bcn.client.run_command('bpy.ops.blenderneuron.add_neon_effect();')
    def test_import_remove_import_again(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc = h.TestCell();')

            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Import the cell and check if created in correct location
            x, y, z = bcn.client.run_command(
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = list(bpy.data.objects['TestCell[0].soma'].location)"
            )

            self.assertAlmostEqual(x, 150, 1)
            self.assertAlmostEqual(y, -176, 1)
            self.assertAlmostEqual(z, 0, 1)

            # Shift the cell up by 100 um
            bcn.client.run_command(
                "bpy.data.objects['TestCell[0].soma'].location = [150, -176, 100];"
            )

            # Re-Import the cell and check if overwrote the shift above
            x, y, z = bcn.client.run_command(
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = list(bpy.data.objects['TestCell[0].soma'].location)"
            )

            self.assertAlmostEqual(z, 0, 1)

            # Shift the cell up again by 100 um
            bcn.client.run_command(
                "bpy.data.objects['TestCell[0].soma'].location = [150, -176, 100];"
            )

            # Remove the group, there should be no cell objects
            count = bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_remove();"
                "return_value = len([ob for ob in bpy.data.objects if 'TestCell[0].soma' in ob.name])"
            )

            # Add a new group, import it
            x, y, z = bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_add();"
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = list(bpy.data.objects['TestCell[0].soma'].location)"
            )

            # The z position should be the original position (not shifted)
            self.assertAlmostEqual(z, 0, 1)

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
    def test_save_groups(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc1 = h.TestCell();')

            # Load cell into first group
            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Create 2nd cell
            ncn.client.run_command('tc2 = h.TestCell();')

            # Add second group, and shift 2nd cell up by 5 um
            bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_add();"
                "bpy.ops.blenderneuron.import_groups();"
                "bpy.data.objects['TestCell[1].soma'].location[2] += 5;")

            file1 = 'test_group1.json'
            file2 = 'test_group2.json'

            try:

                # Test group saving
                bcn.client.run_command(
                    "groups = bpy.types.Object.BlenderNEURON_node.groups;"
                    "groups['Group.000'].to_file('" + file1 + "');"
                    "groups['Group.001'].to_file('" + file2 + "');")

                # Read the saved files
                import json
                with open(file1) as f1, open(file2) as f2:
                    json1 = json.load(f1)
                    json2 = json.load(f2)

                # check that correct cells were saved
                self.assertEqual(json1['roots'][0]['name'], 'TestCell[0].soma')
                self.assertEqual(json2['roots'][0]['name'], 'TestCell[1].soma')

                # Check that the z-location was saved correctly
                self.assertEqual(json1['roots'][0]['coords'][-1], 0)

                # 2nd cell should be shifted up by 5um
                self.assertEqual(json2['roots'][0]['coords'][-1], 5)

            finally:
                os.remove(file1)
                os.remove(file2)

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
예제 #7
0
    def test_blender_coverage(self):

        with NEURON(), Blender(), \
             CommNode('Control-NEURON') as cn, \
             CommNode("Control-Blender") as cb:

            self.assertEqual(cn.client.ping(), 1)
            self.assertEqual(cb.client.ping(), 1)

            cn.client.end_code_coverage()
            cb.client.end_code_coverage()
예제 #8
0
    def test_simulator_settings_exchange(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # set settings in Blender and send to NEURON
            bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "params.neuron_tstop = 1111;"
                "params.time_step = 0.002;"
                "params.abs_tolerance = 0.005;"
                "params.temperature = 44;"
                "params.integration_method = '1';"
                "bpy.ops.blenderneuron.sim_settings_to_neuron();"
                "bpy.ops.blenderneuron.sim_settings_from_neuron();"
            )

            self.assertEqual(1111, bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "return_value = params.neuron_tstop"
            ))

            self.assertAlmostEqual(0.005, bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "return_value = params.abs_tolerance"
            ), places=3)

            self.assertEqual(44, bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "return_value = params.temperature"
            ))

            self.assertEqual('1', bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "return_value = params.integration_method"
            ))

            # Test fixed step integration
            bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "params.time_step = 0.002;"
                "params.integration_method = '0';"
                "bpy.ops.blenderneuron.sim_settings_to_neuron();"
                "bpy.ops.blenderneuron.sim_settings_from_neuron();"
            )

            self.assertAlmostEqual(0.002, bcn.client.run_command(
                "params = bpy.data.scenes['Scene'].BlenderNEURON.simulator_settings;"
                "return_value = params.time_step"
            ), places=3)

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
예제 #9
0
    def test_can_connect_to_neuron(self):
        # Start Blender with a running node
        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # See if Blender is able to connect to the NEURON process node
            result = bcn.client.run_command("return_value = str(bpy.types.Object.BlenderNEURON_node.client)")
            self.assertIn("Proxy", result)



            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
예제 #10
0
    def test_mpi(self):
        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
                Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            # Load TestCell.hoc - create a cell name:mpi rank map
            ncn.client.run_command(
                'h.load_file("tests/TestCell.hoc");'
                'tc = h.TestCell();'
                'from blenderneuron.neuronstart import BlenderNEURON as bn_node;'
            )

            # Get section name before MPI map is provided
            self.assertEqual(
                ncn.client.run_command(
                    'return_value = bn_node.rank_section_name(tc.soma.name());'
                ), 'TestCell[0].soma')

            # Provide MPI map
            ncn.client.run_command(
                'pc = h.ParallelContext();'
                'single_rank_name = "TestCell[0]";'
                'mpi_rank_name =    "TestCell[0]";'
                'mpimap = {};'
                'mpimap[single_rank_name] = { "name": mpi_rank_name, "rank": pc.id() };'
                'mpimap["TestCell[1]"] = { "name": mpi_rank_name, "rank": 2 };'
                'bn_node.init_mpi(pc, mpimap);')

            # Get section name after MPI init
            self.assertEqual(
                ncn.client.run_command(
                    'return_value = bn_node.rank_section_name(tc.soma.name());'
                ), 'TestCell[0].soma')

            # Cell without a specific section
            self.assertEqual(
                ncn.client.run_command(
                    'return_value = bn_node.rank_section_name("TestCell[0]");'
                ), 'TestCell[0]')

            # Try section that does not exist on the rank
            self.assertEqual(
                ncn.client.run_command(
                    'return_value = bn_node.rank_section_name("TestCell[1].soma");'
                ), None)
예제 #11
0
    def test_error_handling(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            from xmlrpc.client import Fault

            try:
                bcn.client.run_command('1/0')
            except Fault:
                pass

            try:
                ncn.client.run_command('1/0')
            except Fault:
                pass

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
예제 #12
0
    def test_group_select_all_none_invert(self):
        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

                # Create a few named root sections in NEURON
                ncn.client.run_command('s1 = h.Section(name="soma1");'
                                       's2 = h.Section(name="soma2");')

                # Refresh the default cell group in Blender
                bcn.client.run_command(
                    "bpy.ops.blenderneuron.get_cell_list_from_neuron();"
                )

                # Check the group contains the two root sections
                self.assertEqual(2, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = len(group.root_entries);"
                ))

                # Unselect all, Check that no cells are selected
                self.assertEqual(True, bcn.client.run_command(
                    "bpy.ops.blenderneuron.unselect_all_cells();"
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = all(cell.selected == False for cell in group.root_entries)"
                ))

                # Select all, Check that all cells are selected
                self.assertEqual(True, bcn.client.run_command(
                    "bpy.ops.blenderneuron.select_all_cells();"
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = all(cell.selected for cell in group.root_entries)"
                ))

                # Unselect last cell, invert selection, and check that first cell is unselected
                self.assertEqual(False, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "group.root_entries[-1].selected = False;"
                    "bpy.ops.blenderneuron.invert_cell_selection();"
                    "return_value = group.root_entries[0].selected"
                ))

                bcn.client.end_code_coverage()
                ncn.client.end_code_coverage()
예제 #13
0
    def test_simulator_run(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # Create a root section in NEURON
            ncn.client.run_command('s1 = h.Section(name="soma1");'
                                   's1.insert("pas");'
                                   's1.insert("hh");')

            # Run the sim
            bcn.client.run_command(
                "bpy.ops.blenderneuron.init_and_run_neuron()"
            )

            # Check that sim time is advanced
            self.assertAlmostEqual(5.0,
                                   ncn.client.run_command('return_value=h.t'),
                                   places=1)

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
    def test_remove_cells_groups(self):

        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
                Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc1 = h.TestCell();')

            # Load cell into first group
            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Create 2nd cell
            ncn.client.run_command('tc2 = h.TestCell();')

            # Add second group, and shift 2nd cell up by 5 um
            group_count = bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_add();"
                "bpy.ops.blenderneuron.import_groups();"
                "bpy.data.objects['TestCell[1].soma'].location[2] += 5;"
                "return_value = len(bpy.context.scene.BlenderNEURON.groups);")

            self.assertEqual(group_count, 2)

            # Unselect 2nd cell from 2nd group and remove the empty group
            group_count = bcn.client.run_command(
                "bpy.context.scene.BlenderNEURON.groups[1].root_entries[1].selected = False;"
                "bpy.ops.blenderneuron.cell_group_remove();"
                "return_value = len(bpy.context.scene.BlenderNEURON.groups);")

            self.assertEqual(group_count, 1)

            # Remove the last group
            group_count = bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_remove();"
                "return_value = len(bpy.context.scene.BlenderNEURON.groups);")

            self.assertEqual(group_count, 0)
예제 #15
0
    def test_mpi_synset(self):
        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
                Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            # Load TestCell.hoc - create a cell name:mpi rank map
            ncn.client.run_command(
                'h.load_file("tests/TestCell.hoc");'
                'tc = h.TestCell();'
                'from blenderneuron.neuronstart import BlenderNEURON as bn_node;'
                'bn_node.update_section_index();')

            # Provide MPI map - first cell on this rank, 2nd cell on another rank
            ncn.client.run_command(
                'pc = h.ParallelContext();'
                'mpimap = {};'
                'mpimap["TestCell[0]"] = { "name": "TestCell[0]", "rank": 0 };'
                'mpimap["TestCell[1]"] = { "name": "TestCell[1]", "rank": 1 };'
                'bn_node.init_mpi(pc, mpimap);')

            ncn.client.run_command('from json import load;'
                                   'f = open("tests/test_synset.json");'
                                   'bn_node.create_synapses(load(f));'
                                   'f.close();')
예제 #16
0
    def test_add_remove_group_and_cells(self):
        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:
                # Create a few named root sections in NEURON
                ncn.client.run_command('s1 = h.Section(name="soma1");'
                                       's2 = h.Section(name="soma2");')

                # Check that a default group was added
                self.assertEqual([1, 0], bcn.client.run_command(
                    "groups = bpy.data.scenes['Scene'].BlenderNEURON.groups;"
                    "count = len(groups);"
                    "index = bpy.data.scenes['Scene'].BlenderNEURON.groups_index;"
                    "return_value = [count, index];"
                ))

                # Refresh the list after adding the cells - this should not break the later tests
                # Check that there are 2 cells within the group
                self.assertEqual(2, bcn.client.run_command(
                    "bpy.ops.blenderneuron.get_cell_list_from_neuron();"
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = len(group.root_entries);"
                ))

                # The last cell listed is the last cell created
                self.assertEqual("soma2", bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = group.root_entries[-1].name"
                ))

                # Check that both cells are selected
                self.assertEqual(True, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = all(cell.selected for cell in group.root_entries)"
                ))

                # Create a second cell group
                # Check that a group was added
                self.assertEqual(2, bcn.client.run_command(
                    "bpy.ops.blenderneuron.cell_group_add();"
                    "return_value = len(bpy.data.scenes['Scene'].BlenderNEURON.groups)"
                ))

                # Check that there are 2 cells within the 2nd group
                self.assertEqual(2, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[1];"
                    "return_value = len(group.root_entries)"
                ))

                # Check that NONE of the cells are selected in 2nd group
                self.assertEqual(True, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[1];"
                    "return_value = all(cell.selected == False for cell in group.root_entries)"
                ))

                # Select the last cell of the *2nd* group
                # Check that the last cell of the *1st* group becomes unselected
                self.assertEqual(False, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[1];"
                    "group.root_entries[-1].selected = True;"
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = group.root_entries[-1].selected"
                ))

                # Now select the first cell of the 2nd group (both cells should be selected)
                # Delete the second cell group (this should free the 2nd group's cells)
                # Check that a group was removed
                self.assertEqual(1, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[1];"
                    "group.root_entries[0].selected = True;"
                    "bpy.ops.blenderneuron.cell_group_remove();"
                    "return_value = len(bpy.data.scenes['Scene'].BlenderNEURON.groups)"
                ))

                # Add a new cell group (it should now contain the free'd cells)
                # Check that a group was added
                self.assertEqual(2, bcn.client.run_command(
                    "bpy.ops.blenderneuron.cell_group_add();"
                    "return_value = len(bpy.data.scenes['Scene'].BlenderNEURON.groups)"
                ))

                # Check that both cells are selected
                self.assertEqual(True, bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[1];"
                    "return_value = all(cell.selected for cell in group.root_entries)"
                ))

                bcn.client.end_code_coverage()
                ncn.client.end_code_coverage()
예제 #17
0
    def test_server_established(self):
        # Start Blender with a running node
        with Blender(), CommNode("Control-Blender") as bcn:
            self.assertEqual(bcn.client.ping(), 1)

            bcn.client.end_code_coverage()
예제 #18
0
    def test_find_synapses(self):

        with NEURON(), CommNode("Control-NEURON", coverage=True) as ncn, \
             Blender(), CommNode("Control-Blender", coverage=True) as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc1 = h.TestCell();')

            # Load cell into first group
            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Create 2nd cell
            ncn.client.run_command('tc2 = h.TestCell();')

            # Add second group, and shift 2nd cell up by 5 um
            bcn.client.run_command(
                "bpy.ops.blenderneuron.cell_group_add();"
                "bpy.ops.blenderneuron.import_groups();"
                "bpy.data.objects['TestCell[1].soma'].location[2] += 5;")

            # Setup synapse former to look for synapses between the two cells
            syn_count = bcn.client.run_command(
                "syn_set = bpy.data.scenes['Scene'].BlenderNEURON.synapse_sets[0];"
                "syn_set.section_pattern_source = '*dend*';"
                "syn_set.group_dest = 'Group.001';"
                "syn_set.is_reciprocal = True;"
                "syn_set.create_spines = True;"
                "syn_set.use_radius = False;"
                "syn_set.max_syns_per_pt = 1;"
                "bpy.ops.blenderneuron.find_synapse_locations();"
                "return_value = len(bpy.data.objects['SynapsePreview'].data.splines)"
            )

            self.assertEqual(syn_count, 32)

            # There should be no synapses when max search distance is very small
            syn_count = bcn.client.run_command(
                "syn_set = bpy.data.scenes['Scene'].BlenderNEURON.synapse_sets[0];"
                "syn_set.max_distance = 1;"
                "bpy.ops.blenderneuron.find_synapse_locations();"
                "return_value = len(bpy.data.objects['SynapsePreview'].data.splines)"
            )

            self.assertEqual(syn_count, 0)

            # When using radius, it should find the same synapses at a shortened distance
            syn_count = bcn.client.run_command(
                "syn_set = bpy.data.scenes['Scene'].BlenderNEURON.synapse_sets[0];"
                "syn_set.use_radius = True;"
                "syn_set.max_distance = 4;"
                "bpy.ops.blenderneuron.find_synapse_locations();"
                "return_value = len(bpy.data.objects['SynapsePreview'].data.splines)"
            )

            self.assertEqual(syn_count, 32)

            # Sub-divide the cell sections in NEURON
            ncn.client.run_command(
                "[setattr(sec,'nseg', 10) for sec in h.allsec()];")

            # And create the found synapses
            bcn.client.run_command("bpy.ops.blenderneuron.create_synapses();")

            # check if the syns were created correctly in NEURON
            syn1_sec, syn1_x = ncn.client.run_command(
                'seg = h.ExpSyn[0].get_segment();'
                'return_value = (seg.sec.name(), seg.x);')

            self.assertEqual(syn1_sec, 'TestCell[1].dendrites[4]')
            self.assertEqual(syn1_x, 0.95)  # middle of the 10th segment = 0.95

            syn2_sec, syn2_x = ncn.client.run_command(
                'seg = h.ExpSyn[1].get_segment();'
                'return_value = (seg.sec.name(), seg.x);')

            self.assertEqual(syn2_sec, 'SynapseSet_001_Spine[0].head')
            self.assertEqual(syn2_x, 0.5)  # middle of the spine head

            # Save the found synapses into a JSON file
            syn_file = 'syn_test.json'

            bcn.client.run_command(
                "bpy.context.scene.BlenderNEURON.synapse_set.save_synapses('" +
                syn_file + "');")

            import json
            with open(syn_file) as f:
                syns = json.load(f)

            import os
            try:
                os.remove(syn_file)
            except:
                pass

            self.assertEqual(syns['entries'][0]['dest_section'],
                             'TestCell[1].dendrites[4]')
            self.assertEqual(syns['entries'][0]['source_section'],
                             'TestCell[0].dendrites[4]')
            self.assertEqual(syns['entries'][0]['dest_x'], 1.0)
            self.assertEqual(syns['entries'][0]['source_x'], 1.0)

            # Test renaming logic
            syn_set_name = bcn.client.run_command(
                "syn_set = bpy.data.scenes['Scene'].BlenderNEURON.synapse_sets[0];"
                "syn_set.name = 'TEST_NAME';"
                "return_value = syn_set.name;")

            self.assertEqual(syn_set_name, 'TEST_NAME')
    def test_import_export_cell(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # Load TestCell.hoc - create a group
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc = h.TestCell();')

            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # Check that the cell was loaded
            self.assertEqual(
                "TestCell[0].soma",
                bcn.client.run_command(
                    "group = bpy.data.scenes['Scene'].BlenderNEURON.groups['Group.000'];"
                    "return_value = group.root_entries[0].name"))

            # Import the cell and check if created in correct location
            x, y, z = bcn.client.run_command(
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = list(bpy.data.objects['TestCell[0].soma'].location)"
            )

            self.assertAlmostEqual(x, 150, 1)
            self.assertAlmostEqual(y, -176, 1)
            self.assertAlmostEqual(z, 0, 1)

            # And cell bounding box dimensions are correct
            dim_x, dim_y, dim_z = bcn.client.run_command(
                "return_value = list(bpy.data.objects['TestCell[0].soma'].dimensions)"
            )

            self.assertAlmostEqual(dim_x, 165.45, 1)
            self.assertAlmostEqual(dim_y, 165.80, 1)
            self.assertAlmostEqual(dim_z, 1, 0)

            # Shift the cell up by 100 um, and export to NRN
            bcn.client.run_command(
                "bpy.data.objects['TestCell[0].soma'].location = [150, -176, 100];"
                "bpy.ops.blenderneuron.update_groups_with_view_data();"
                "bpy.ops.blenderneuron.export_groups();")

            # Test if coordinates in NRN changed by same amount
            z_soma, z_dend1, z_dend2 = ncn.client.run_command(
                'return_value = [h.z3d(0,sec=tc.soma), h.z3d(0,sec=tc.dendrites[-1]), h.z3d(0,sec=tc.dendrites[15])]'
            )

            self.assertEqual(z_soma, 100.0)
            self.assertEqual(z_soma, z_dend1)
            self.assertEqual(z_dend1, z_dend2)

            # Re-import the cell and check if change persists
            x, y, z = bcn.client.run_command(
                "bpy.ops.blenderneuron.import_groups();"
                "return_value = list(bpy.data.objects['TestCell[0].soma'].location)"
            )

            self.assertAlmostEqual(z, 100, 1)

            # And that duplicate objects were not created
            count = bcn.client.run_command(
                "cellObjs = [ob for ob in bpy.data.objects if 'TestCell[0].soma' in ob.name];"
                "return_value = len(cellObjs)")

            self.assertEqual(count, 1)

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()
    def test_object_levels(self):

        with NEURON(), CommNode("Control-NEURON") as ncn, \
             Blender(), CommNode("Control-Blender") as bcn:

            # Load TestCell.hoc - and add somatic stimulus
            ncn.client.run_command('h.load_file("tests/TestCell.hoc");'
                                   'tc = h.TestCell();'
                                   'ic = h.IClamp(0.5);'
                                   'ic.amp = 1; ic.delay = 1; ic.dur = 10;')

            bcn.client.run_command(
                "bpy.ops.blenderneuron.get_cell_list_from_neuron();")

            # ----------------- Cell level objects --------------- #
            soma_object_exists, dendrite_object_exists = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].interaction_granularity = 'Cell';"
                "bpy.ops.blenderneuron.import_groups();"
                "objs = bpy.data.objects;"
                "return_value = ('TestCell[0].soma' in objs, 'TestCell[0].dendrites[0]' in objs);"
            )

            self.assertTrue(soma_object_exists)
            self.assertFalse(dendrite_object_exists)

            # Import animation - animate WHOLE CELL
            soma_mat_exists, dendrite_mat_exists, = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].record_activity = True;"
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].recording_granularity = 'Cell';"
                "bpy.ops.blenderneuron.import_groups();"
                "mats = bpy.data.materials;"
                "return_value = ('TestCell[0].soma' in mats, 'TestCell[0].dendrites[0]' in mats);"
            )

            self.assertTrue(soma_mat_exists)
            self.assertFalse(dendrite_mat_exists)

            soma_emission_start, soma_emission_end, = bcn.client.run_command(
                "mats = bpy.data.materials;"
                "bpy.context.scene.frame_set(0);"
                "start_emit = mats['TestCell[0].soma'].emit;"
                "bpy.context.scene.frame_set(5);"
                "end_emit = mats['TestCell[0].soma'].emit;"
                "return_value = (start_emit, end_emit);")

            self.assertGreater(soma_emission_start, 0)
            self.assertGreater(soma_emission_end, soma_emission_start)

            # Import animation - animate EACH SECTION
            soma_mat_exists, dendrite_mat_exists, = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].record_activity = True;"
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].recording_granularity = 'Section';"
                "bpy.ops.blenderneuron.import_groups();"
                "mats = bpy.data.materials;"
                "return_value = ('TestCell[0].soma' in mats, 'TestCell[0].dendrites[0]' in mats);"
            )

            self.assertTrue(soma_mat_exists)
            self.assertTrue(dendrite_mat_exists)

            dend_emission_start, dend_emission_end, = bcn.client.run_command(
                "mats = bpy.data.materials;"
                "bpy.context.scene.frame_set(0);"
                "start_emit = mats['TestCell[0].dendrites[0]'].emit;"
                "bpy.context.scene.frame_set(5);"
                "end_emit = mats['TestCell[0].dendrites[0]'].emit;"
                "return_value = (start_emit, end_emit);")

            self.assertGreater(dend_emission_start, 0)
            self.assertGreater(dend_emission_end, dend_emission_start)

            # --------------- Section level objects ------------------- #
            soma_object_exists, dendrite_object_exists = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].interaction_granularity = 'Section';"
                "bpy.ops.blenderneuron.import_groups();"
                "objs = bpy.data.objects;"
                "return_value = ('TestCell[0].soma' in objs, 'TestCell[0].dendrites[0]' in objs);"
            )

            self.assertTrue(soma_object_exists)
            self.assertTrue(dendrite_object_exists)

            # Import animation - animate WHOLE CELL
            soma_mat_exists, dendrite_mat_exists, = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].record_activity = True;"
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].recording_granularity = 'Cell';"
                "bpy.ops.blenderneuron.import_groups();"
                "mats = bpy.data.materials;"
                "return_value = ('TestCell[0].soma' in mats, 'TestCell[0].dendrites[0]' in mats);"
            )

            self.assertTrue(soma_mat_exists)
            self.assertFalse(dendrite_mat_exists)

            soma_emission_start, soma_emission_end, = bcn.client.run_command(
                "mats = bpy.data.materials;"
                "bpy.context.scene.frame_set(0);"
                "start_emit = mats['TestCell[0].soma'].emit;"
                "bpy.context.scene.frame_set(5);"
                "end_emit = mats['TestCell[0].soma'].emit;"
                "return_value = (start_emit, end_emit);")

            self.assertGreater(soma_emission_start, 0)
            self.assertGreater(soma_emission_end, soma_emission_start)

            # Import animation - animate EACH SECTION
            soma_mat_exists, dendrite_mat_exists, = bcn.client.run_command(
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].record_activity = True;"
                "bpy.data.scenes['Scene'].BlenderNEURON.groups[0].recording_granularity = 'Section';"
                "bpy.ops.blenderneuron.import_groups();"
                "mats = bpy.data.materials;"
                "return_value = ('TestCell[0].soma' in mats, 'TestCell[0].dendrites[0]' in mats);"
            )

            self.assertTrue(soma_mat_exists)
            self.assertTrue(dendrite_mat_exists)

            dend_emission_start, dend_emission_end, = bcn.client.run_command(
                "mats = bpy.data.materials;"
                "bpy.context.scene.frame_set(0);"
                "start_emit = mats['TestCell[0].dendrites[0]'].emit;"
                "bpy.context.scene.frame_set(5);"
                "end_emit = mats['TestCell[0].dendrites[0]'].emit;"
                "return_value = (start_emit, end_emit);")

            self.assertGreater(dend_emission_start, 0)
            self.assertGreater(dend_emission_end, dend_emission_start)

            # Test group renaming logic
            group_name = bcn.client.run_command(
                "group = bpy.data.scenes['Scene'].BlenderNEURON.groups[0];"
                "group.name = 'TEST_GROUP';"
                "return_value = group.name;")

            self.assertEqual(group_name, 'TEST_GROUP')

            bcn.client.end_code_coverage()
            ncn.client.end_code_coverage()