def test_remove_unifurcations(): m = Morphology() section = m.append_root_section( PointLevel([[1, 0, 0], [2, 0, 0]], [2, 2], [20, 20]), SectionType.axon) section.append_section(PointLevel([[2, 0, 0], [3, 0, 0]], [2, 2], [20, 20])) with captured_output() as (_, err): with ostream_redirect(stdout=True, stderr=True): m.remove_unifurcations() assert len(list(m.iter())) == 1 assert ( err.getvalue().strip() == 'Warning: section 1 is the only child of section: 0\nIt will be merged ' 'with the parent section') # Checking that remove_unifurcations() issues a warning on missing duplicate m = Morphology() section = m.append_root_section( PointLevel([[1, 0, 0], [2, 0, 0]], [2, 2], [20, 20]), SectionType.axon) section.append_section(PointLevel([[2, 0, 0], [3, 0, 0]], [2, 2], [20, 20])) with captured_output() as (_, err): with ostream_redirect(): section.append_section( PointLevel([[2, 1, 0], [2, 0, 0]], [2, 2], [20, 20])) with captured_output() as (_, err): with ostream_redirect(): m.remove_unifurcations() assert ( err.getvalue().strip() == 'Warning: while appending section: 2 to parent: 0\nThe section first point should be parent section last point: \n : X Y Z Diameter\nparent last point :[2.000000, 0.000000, 0.000000, 2.000000]\nchild first point :[2.000000, 1.000000, 0.000000, 2.000000]' )
def test_nested_single_child(): with captured_output() as (_, err): with ostream_redirect(stdout=True, stderr=True): n = Morphology(DATA_DIR / 'nested_single_children.asc') n.remove_unifurcations() assert_array_equal( n.root_sections[0].points, [[0., 0., 0.], [0., 0., 1.], [0., 0., 2.], [0., 0., 3.], [0., 0., 4.]]) assert_array_equal(n.root_sections[0].diameters, np.array([8, 7, 6, 5, 4], dtype=np.float32))
def test_empty_sibling(): '''The empty sibling will be removed and the single child will be merged with its parent''' with captured_output() as (_, err): with ostream_redirect(stdout=True, stderr=True): with tmp_asc_file('''((Dendrite) (3 -4 0 10) (3 -6 0 9) (3 -8 0 8) (3 -10 0 7) ( (3 -10 0 6) (0 -10 0 5) (-3 -10 0 4) | ; <-- empty sibling but still works ! ) ) ''') as tmp_file: n = Morphology(tmp_file.name) n.remove_unifurcations() assert_substring('is the only child of section: 0', err.getvalue().strip()) assert_substring('It will be merged with the parent section', err.getvalue().strip()) assert len(n.root_sections) == 1 assert_array_equal( n.root_sections[0].points, np.array([[3, -4, 0], [3, -6, 0], [3, -8, 0], [3, -10, 0], [0, -10, 0], [-3, -10, 0]], dtype=np.float32)) assert_array_equal(n.root_sections[0].diameters, np.array([10, 9, 8, 7, 5, 4], dtype=np.float32)) assert len(n.annotations) == 1 annotation = n.annotations[0] assert annotation.type == morphio.AnnotationType.single_child assert annotation.line_number == -1 assert_array_equal(annotation.points, [[3, -10, 0], [0, -10, 0], [-3, -10, 0]]) assert_array_equal(annotation.diameters, [6, 5, 4])
def test_annotation(): with captured_output() as (_, err): with ostream_redirect(stdout=True, stderr=True): with tmp_asc_file("""((Dendrite) (3 -4 0 2) (3 -6 0 2) (3 -8 0 2) (3 -10 0 2) ( (3 -10 0 2) (0 -10 0 2) (-3 -10 0 2) | ; <-- empty sibling but still works ! ) ) """) as tmp_file: cell = Morphology(tmp_file.name) cell.remove_unifurcations() for n in (cell, cell.as_immutable(), cell.as_immutable().as_mutable()): assert len(n.annotations) == 1 annotation = n.annotations[0] assert annotation.type == morphio.AnnotationType.single_child
def sanitize(input_neuron, output_path): '''Sanitize one morphology. - ensures it can be loaded with MorphIO - raises if the morphology has no soma or of invalid format - removes unifurcations - set negative diameters to zero - raises if the morphology has a neurite whose type changes along the way - removes segments with near zero lengths (shorter than 1e-4) Args: input_neuron (str|pathlib.Path|morphio.Morphology|morphio.mut.Morphology): input neuron output_path (str|pathlib.Path): output name ''' neuron = Morphology(input_neuron) if neuron.soma.type == SomaType.SOMA_UNDEFINED: # pylint: disable=no-member raise CorruptedMorphology( '{} has an invalid or no soma'.format(input_neuron)) neuron.remove_unifurcations() for section in neuron.iter(): section.diameters = np.clip(section.diameters, 0, None) for root in neuron.root_sections: # pylint: disable=not-an-iterable for section in root.iter(): if section.type != root.type: raise CorruptedMorphology( f'{input_neuron} has a neurite whose type changes along ' 'the way\n' f'Child section (id: {section.id}) has a different type ' f'({section.type}) than its parent (id: ' f'{section.parent.id}) (type: {section.parent.type})') fix_non_zero_segments(neuron).write(str(output_path))
def convert(input_file, outputfile, recenter=False, nrn_order=False, single_point_soma=False, sanitize=False): '''Run the appropriate converter Args: input_file(str): path to input file outputfile(str): path to output file recenter(bool): whether to recenter the morphology based on the center of gravity of the soma nrn_order(bool): whether to traverse the neuron in the NEURON fashion single_point_soma(bool): For SWC only sanitize(bool): whether to sanitize the morphology ''' kwargs = {} if nrn_order: kwargs['options'] = Option.nrn_order neuron = Morphology(input_file, **kwargs) if sanitize: neuron.remove_unifurcations() output_ext = Path(outputfile).suffix if single_point_soma and output_ext.lower() != '.swc': raise Exception('Single point soma is only applicable for swc output') if output_ext.lower() not in ( '.swc', '.asc', '.h5', ): raise Exception('Output file format should be one swc, asc or h5') output_ext = output_ext[1:] # Remove the dot try: converter = { 'swc': from_swc, 'asc': from_h5_or_asc, 'h5': from_h5_or_asc, }[neuron.version[0]] except KeyError as e: raise Exception('No converter for morphology type: {}'.format( neuron.version)) from e L.info('Original soma type: %s', neuron.soma_type) new = converter(neuron, output_ext) if single_point_soma: soma_to_single_point(new.soma) if recenter: transform.translate(new, -1 * new.soma.center) try: new.write(outputfile) except WriterError as e: raise MorphToolException('Use `sanitize` option for converting') from e try: # pylint: disable=import-outside-toplevel from morph_tool.neuron_surface import get_NEURON_surface L.info( 'Soma surface as computed by NEURON:\n' 'before conversion: %s\n' 'after conversion: %s', get_NEURON_surface(input_file), get_NEURON_surface(outputfile)) except: # noqa pylint: disable=bare-except L.info( 'Final NEURON soma surface check was skipped probably because BluePyOpt' ' or NEURON is not installed')