diff --git a/bluepysnap/morph.py b/bluepysnap/morph.py index 0f200c90..cc24c128 100644 --- a/bluepysnap/morph.py +++ b/bluepysnap/morph.py @@ -20,8 +20,8 @@ from pathlib import Path import morph_tool.transform as transformations +import morphio import numpy as np -from morphio.mut import Morphology from bluepysnap.exceptions import BluepySnapError from bluepysnap.sonata_constants import Node @@ -51,8 +51,8 @@ def __init__(self, morph_dir, population, alternate_morphologies=None): self._alternate_morphologies = alternate_morphologies or {} self._population = population - def get_morphology_dir(self, extension): - """Return morphology directory based on a given extension.""" + def _get_morphology_base(self, extension): + """Get morphology base path; this will be a directory unless it's a morphology container""" if extension == "swc": if not self._morph_dir: raise BluepySnapError("'morphologies_dir' is not defined in config") @@ -68,6 +68,25 @@ def get_morphology_dir(self, extension): return morph_dir + def get_morphology_dir(self, extension="swc"): + """Return morphology directory based on a given extension.""" + morph_dir = self._get_morphology_base(extension) + + if extension == "h5" and Path(morph_dir).is_file(): + raise BluepySnapError( + f"'{morph_dir}' is a morphology container, so a directory does not exist" + ) + + return morph_dir + + def get_name(self, node_id): + """Get the morphology name for a `node_id`.""" + if not is_node_id(node_id): + raise BluepySnapError("node_id must be a int or a CircuitNodeId") + + name = self._population.get(node_id, Node.MORPHOLOGY) + return name + def get_filepath(self, node_id, extension="swc"): """Return path to SWC morphology file corresponding to `node_id`. @@ -75,9 +94,7 @@ def get_filepath(self, node_id, extension="swc"): node_id (int/CircuitNodeId): could be a int or CircuitNodeId. extension (str): expected filetype extension of the morph file. """ - if not is_node_id(node_id): - raise BluepySnapError("node_id must be a int or a CircuitNodeId") - name = self._population.get(node_id, Node.MORPHOLOGY) + name = self.get_name(node_id) return Path(self.get_morphology_dir(extension), f"{name}.{extension}") @@ -90,11 +107,18 @@ def get(self, node_id, transform=False, extension="swc"): according to `node_id` position in the circuit. extension (str): expected filetype extension of the morph file. """ - filepath = self.get_filepath(node_id, extension=extension) - result = Morphology(filepath) + if extension == "h5": + collection = morphio.Collection(self._get_morphology_base(extension)) + name = self.get_name(node_id) + result = collection.load(name, mutable=True) + else: + filepath = self.get_filepath(node_id, extension=extension) + result = morphio.mut.Morphology(filepath) + if transform: T = np.eye(4) T[:3, :3] = self._population.orientations(node_id) # rotations T[:3, 3] = self._population.positions(node_id).values # translations transformations.transform(result, T) + return result.as_immutable() diff --git a/tests/data/morphologies/container-morphs.h5 b/tests/data/morphologies/container-morphs.h5 new file mode 100644 index 00000000..967c33fc Binary files /dev/null and b/tests/data/morphologies/container-morphs.h5 differ diff --git a/tests/data/morphologies/morph-B.h5 b/tests/data/morphologies/morph-B.h5 index e69de29b..6b65c705 100644 Binary files a/tests/data/morphologies/morph-B.h5 and b/tests/data/morphologies/morph-B.h5 differ diff --git a/tests/test_morph.py b/tests/test_morph.py index 5ede964e..6182ef5b 100644 --- a/tests/test_morph.py +++ b/tests/test_morph.py @@ -126,6 +126,25 @@ def test_get_morphology(self): with pytest.raises(BluepySnapError, match="node_id must be a int or a CircuitNodeId"): self.test_obj.get([0, 1]) + def test_get_alternate_morphology_collection(self): + morph_path = TEST_DATA_DIR / "morphologies/container-morphs.h5" + alternate_morphs = {"h5v1": str(morph_path)} + + test_obj = test_module.MorphHelper( + None, self.nodes, alternate_morphologies=alternate_morphs + ) + + node_id = 0 + + with pytest.raises(BluepySnapError): + test_obj.get_morphology_dir(extension="h5") + + with pytest.raises(BluepySnapError): + test_obj.get_filepath(node_id, extension="h5") + + morph_A = test_obj.get(node_id, extension="h5") + assert len(morph_A.points) == 13 + def test_get_alternate_morphology(self): alternate_morphs = {"h5v1": str(self.morph_path)} test_obj = test_module.MorphHelper(