diff --git a/pyneuroml/plot/PlotMorphology.py b/pyneuroml/plot/PlotMorphology.py index e3f3d777..c036a039 100644 --- a/pyneuroml/plot/PlotMorphology.py +++ b/pyneuroml/plot/PlotMorphology.py @@ -18,7 +18,7 @@ import matplotlib import numpy from matplotlib import pyplot as plt -from neuroml import Cell, NeuroMLDocument, SegmentGroup +from neuroml import Cell, Morphology, NeuroMLDocument, SegmentGroup from neuroml.neuro_lex_ids import neuro_lex_ids from pyneuroml.pynml import read_neuroml2_file @@ -301,6 +301,7 @@ def plot_2D( if verbose: print("Plotting %s" % nml_file) + # if it's a file, load it first if isinstance(nml_file, str): # load without optimization for older HDF5 API # TODO: check if this is required: must for MultiscaleISN @@ -315,30 +316,35 @@ def plot_2D( optimized=True, ) load_minimal_morphplottable__model(nml_model, nml_file) + # note that from this point, the model object is not necessarily valid, + # because we've removed lots of bits. + else: + nml_model = nml_file + # if it isn't a NeuroMLDocument, create one + if isinstance(nml_model, Cell): + logger.info("Got a cell") + plottable_nml_model = NeuroMLDocument(id="newdoc") + plottable_nml_model.add(nml_model) + logger.info(f"plottable cell model is: {plottable_nml_model.cells[0]}") if title is None: - try: - title = f"{nml_model.networks[0].id} from {nml_file}" - except IndexError: - title = f"{nml_model.cells[0].id} from {nml_file}" - - elif isinstance(nml_file, Cell): - nml_model = NeuroMLDocument(id="newdoc") - nml_model.add(nml_file) + title = f"{plottable_nml_model.cells[0].id}" + + # if it's only a cell, add it to an empty cell in a document + elif isinstance(nml_model, Morphology): + logger.info("Received morph, adding to a dummy cell") + plottable_nml_model = NeuroMLDocument(id="newdoc") + nml_cell = plottable_nml_model.add( + Cell, id=nml_model.id, morphology=nml_model, validate=False + ) + plottable_nml_model.add(nml_cell) + logger.info(f"plottable cell model is: {plottable_nml_model.cells[0]}") if title is None: - title = f"{nml_model.cells[0].id}" - - elif isinstance(nml_file, NeuroMLDocument): - nml_model = nml_file + title = f"{plottable_nml_model.cells[0].id}" + elif isinstance(nml_model, NeuroMLDocument): + plottable_nml_model = nml_model if title is None: - try: - title = f"{nml_model.networks[0].id} from {nml_file.id}" - except IndexError: - title = f"{nml_model.cells[0].id} from {nml_file.id}" - else: - raise TypeError( - "Passed model is not a NeuroML file path, nor a neuroml.Cell, nor a neuroml.NeuroMLDocument" - ) + title = f"{plottable_nml_model.id}" ( cell_id_vs_cell, @@ -346,7 +352,7 @@ def plot_2D( positions, pop_id_vs_color, pop_id_vs_radii, - ) = extract_position_info(nml_model, verbose) + ) = extract_position_info(plottable_nml_model, verbose) if verbose: logger.debug(f"positions: {positions}") diff --git a/pyneuroml/plot/PlotMorphologyVispy.py b/pyneuroml/plot/PlotMorphologyVispy.py index 5ebcb336..84cddb19 100644 --- a/pyneuroml/plot/PlotMorphologyVispy.py +++ b/pyneuroml/plot/PlotMorphologyVispy.py @@ -17,7 +17,7 @@ import numpy import progressbar -from neuroml import Cell, NeuroMLDocument, SegmentGroup +from neuroml import Cell, Morphology, NeuroMLDocument, SegmentGroup from neuroml.neuro_lex_ids import neuro_lex_ids from scipy.spatial.transform import Rotation @@ -302,7 +302,7 @@ def vispy_on_key_press(event): def plot_interactive_3D( - nml_file: typing.Union[str, Cell, NeuroMLDocument], + nml_file: typing.Union[str, Cell, Morphology, NeuroMLDocument], min_width: float = DEFAULTS["minWidth"], verbose: bool = False, plot_type: str = "constant", @@ -335,8 +335,10 @@ def plot_interactive_3D( :param nml_file: path to NeuroML cell file or - :py:class:`neuroml.NeuroMLDocument` or :py:class:`neuroml.Cell` object - :type nml_file: str or neuroml.NeuroMLDocument or neuroml.Cell + :py:class:`neuroml.NeuroMLDocument` or :py:class:`neuroml.Cell` + or :py:class:`neuroml.Morphology` object + :type nml_file: str or neuroml.NeuroMLDocument or neuroml.Cell or + neuroml.Morphology :param min_width: minimum width for segments (useful for visualising very thin segments): default 0.8um :type min_width: float @@ -449,6 +451,7 @@ def plot_interactive_3D( if verbose: logger.info(f"Visualising {nml_file}") + # if it's a file, load it first if isinstance(nml_file, str): # load without optimization for older HDF5 API # TODO: check if this is required: must for MultiscaleISN @@ -463,30 +466,35 @@ def plot_interactive_3D( optimized=True, ) load_minimal_morphplottable__model(nml_model, nml_file) + # note that from this point, the model object is not necessarily valid, + # because we've removed lots of bits. + else: + nml_model = nml_file + # if it isn't a NeuroMLDocument, create one + if isinstance(nml_model, Cell): + logger.info("Got a cell") + plottable_nml_model = NeuroMLDocument(id="newdoc") + plottable_nml_model.add(nml_model) + logger.info(f"plottable cell model is: {plottable_nml_model.cells[0]}") if title is None: - try: - title = f"{nml_model.networks[0].id} from {nml_file}" - except IndexError: - title = f"{nml_model.cells[0].id} from {nml_file}" - - elif isinstance(nml_file, Cell): - nml_model = NeuroMLDocument(id="newdoc") - nml_model.add(nml_file) + title = f"{plottable_nml_model.cells[0].id}" + + # if it's only a cell, add it to an empty cell in a document + elif isinstance(nml_model, Morphology): + logger.info("Received morph, adding to a dummy cell") + plottable_nml_model = NeuroMLDocument(id="newdoc") + nml_cell = plottable_nml_model.add( + Cell, id=nml_model.id, morphology=nml_model, validate=False + ) + plottable_nml_model.add(nml_cell) + logger.info(f"plottable cell model is: {plottable_nml_model.cells[0]}") if title is None: - title = f"{nml_model.cells[0].id}" - - elif isinstance(nml_file, NeuroMLDocument): - nml_model = nml_file + title = f"{plottable_nml_model.cells[0].id}" + elif isinstance(nml_model, NeuroMLDocument): + plottable_nml_model = nml_model if title is None: - try: - title = f"{nml_model.networks[0].id} from {nml_file.id}" - except IndexError: - title = f"{nml_model.cells[0].id} from {nml_file.id}" - else: - raise TypeError( - "Passed model is not a NeuroML file path, nor a neuroml.Cell, nor a neuroml.NeuroMLDocument" - ) + title = f"{plottable_nml_model.id}" ( cell_id_vs_cell, @@ -494,7 +502,7 @@ def plot_interactive_3D( positions, pop_id_vs_color, pop_id_vs_radii, - ) = extract_position_info(nml_model, verbose) + ) = extract_position_info(plottable_nml_model, verbose) logger.debug(f"positions: {positions}") logger.debug(f"pop_id_vs_cell: {pop_id_vs_cell}") @@ -726,7 +734,7 @@ def plot_interactive_3D( f"More meshes than threshold ({len(meshdata.keys())}/{precision[1]}), reducing precision to {precision[0]} and re-calculating." ) plot_interactive_3D( - nml_file=nml_model, + nml_file=plottable_nml_model, min_width=min_width, verbose=verbose, plot_type=plot_type, diff --git a/pyneuroml/utils/__init__.py b/pyneuroml/utils/__init__.py index 3ae2701a..18957c13 100644 --- a/pyneuroml/utils/__init__.py +++ b/pyneuroml/utils/__init__.py @@ -25,13 +25,13 @@ import neuroml import numpy -import pyneuroml.utils.misc from lems.model.model import Model from neuroml.loaders import read_neuroml2_file + +import pyneuroml.utils.misc from pyneuroml.errors import UNKNOWN_ERR from pyneuroml.utils.plot import get_next_hex_color - logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -86,12 +86,20 @@ def extract_position_info( pop_id_vs_color = {} pop_id_vs_radii = {} + morph_elements = [] cell_elements = [] popElements = [] cell_elements.extend(nml_model.cells) cell_elements.extend(nml_model.cell2_ca_poolses) + # handle morphology elements by adding them into dummy cells + ctr = 1 + morph_elements.extend(nml_model.morphology) + for m in morph_elements: + cell_elements.append(neuroml.Cell(id=f"Dummy cell {ctr}", morphology=m)) + ctr += 1 + # if the model does not include a network, plot all the cells in the # model in new dummy populations if len(nml_model.networks) == 0: diff --git a/pyneuroml/utils/plot.py b/pyneuroml/utils/plot.py index ee677d4e..4a862374 100644 --- a/pyneuroml/utils/plot.py +++ b/pyneuroml/utils/plot.py @@ -352,6 +352,7 @@ def load_minimal_morphplottable__model( required_members = [ "id", "cells", + "morphology", "cell2_ca_poolses", "networks", "populations", @@ -360,6 +361,7 @@ def load_minimal_morphplottable__model( for m in model_members: if m not in required_members: setattr(nml_model, m, None) + logger.debug(f"Dropped {m}") # if the model contains a network, use it if len(nml_model.networks) > 0: @@ -385,7 +387,7 @@ def load_minimal_morphplottable__model( acell.biophysical_properties = None nml_model.add(acell) else: - # add any included cells to the main document + # add any included cells or morphologies to the main document for inc in nml_model.includes: incl_loc = os.path.abspath(os.path.join(base_path, inc.href)) if os.path.isfile(incl_loc): @@ -393,3 +395,5 @@ def load_minimal_morphplottable__model( for acell in inc.cells: acell.biophysical_properties = None nml_model.add(acell) + for morph in inc.morphology: + nml_model.add(morph) diff --git a/tests/plot/L23-example/HL23VIP.morph.cell.nml b/tests/plot/L23-example/HL23VIP.morph.cell.nml new file mode 100644 index 00000000..97906cc7 --- /dev/null +++ b/tests/plot/L23-example/HL23VIP.morph.cell.nml @@ -0,0 +1,5000 @@ + + Truncated cell morphology + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/plot/test_morphology_plot.py b/tests/plot/test_morphology_plot.py index 4d709010..81cadad2 100644 --- a/tests/plot/test_morphology_plot.py +++ b/tests/plot/test_morphology_plot.py @@ -278,6 +278,12 @@ def test_3d_morphology_plotter_vispy_network_with_spec2(self): }, ) + @pytest.mark.localonly + def test_3d_plotter_vispy_morph_only(self): + """Test plot_interactive_3D function with morphology only NeuroML document.""" + nml_file = "tests/plot/L23-example/HL23VIP.morph.cell.nml" + plot_interactive_3D(nml_file) + @pytest.mark.localonly def test_3d_plotter_vispy(self): """Test plot_3D_cell_morphology_vispy function."""