Skip to content

Commit

Permalink
feat(pynml): add functions to list exposures and list recording paths
Browse files Browse the repository at this point in the history
Should help with NeuroML/Documentation#15

TODO: the LEMS functions that this wraps around seem to be incomplete. I
don't think the exposures listed there include ones inherited from their
superclasses/ancestors.
  • Loading branch information
sanjayankur31 committed Jul 21, 2021
1 parent 7ca4b9b commit 6b8ea6c
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
72 changes: 71 additions & 1 deletion pyneuroml/pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import matplotlib
import lems.model.model as lems_model
from lems.model.fundamental import Include
from lems.parser.LEMS import LEMSFileParser

from pyneuroml import __version__
Expand Down Expand Up @@ -368,6 +369,75 @@ def extract_lems_definition_files(path=None):
return path


def list_exposures(nml_doc_fn, substring=None):
# type: (str, str) -> typing.Union[typing.Dict[lems_model.component.Component, typing.List[lems_model.component.Exposure]], None]
"""List exposures in a NeuroML model document file.
This wraps around `lems.model.list_exposures` to list the exposures in a
NeuroML2 model. The only difference between the two is that the
`lems.model.list_exposures` function is not aware of the NeuroML2 component
types (since it's for any LEMS models in general), but this one is.
:param nml_doc_fn: NeuroML2 file to list exposures for
:type nml_doc: str
:param substring: substring to match for in component names
:type substring: str
:returns: dictionary of components and their exposures.
The returned dictionary is of the form:
..
{
"component": ["exp1", "exp2"]
}
"""
return get_standalone_lems_model(nml_doc_fn).list_exposures(substring)


def list_recording_paths(nml_doc_fn, substring):
# type: (str, str) -> typing.List[str]
"""List the recording path strings for exposures.
This wraps around `lems.model.list_recording_paths` to list the recording
paths in the given NeuroML2 model. The only difference between the two is
that the `lems.model.list_recording_paths` function is not aware of the
NeuroML2 component types (since it's for any LEMS models in general), but
this one is.
:param nml_doc_fn: NeuroML2 file to list recording paths for
:type nml_doc: str
:param substring: substring to match component ids against
:type substring: str
:returns: list of recording paths
"""
return get_standalone_lems_model(nml_doc_fn).list_recording_paths(substring)


def get_standalone_lems_model(nml_doc_fn):
# type: (str) -> lems_model.Model
"""Get the complete, expanded LEMS model.
This function takes a NeuroML2 file, includes all the NeuroML2 LEMS
definitions in it and generates the complete, standalone LEMS model.
:param nml_doc_fn: name of NeuroML file to expand
:type nml_doc_fn: str
:returns: complete LEMS model
"""
new_lems_model = lems_model.Model(include_includes=True,
fail_on_missing_includes=True)
new_lems_model.debug = True
neuroml2_defs_dir = extract_lems_definition_files()
filelist = os.listdir(neuroml2_defs_dir)
for nml_lems_f in filelist:
new_lems_model.include_file(neuroml2_defs_dir + nml_lems_f,
[neuroml2_defs_dir])
new_lems_model.include_file(nml_doc_fn, [""])
shutil.rmtree(neuroml2_defs_dir)
return new_lems_model


def split_nml2_quantity(nml2_quantity):
# type: (str) -> typing.Tuple[float, str]
Expand All @@ -392,7 +462,7 @@ def split_nml2_quantity(nml2_quantity):


def get_value_in_si(nml2_quantity):
# type: (str) -> float
# type: (str) -> typing.Union[float, None]
"""Get value of a NeuroML2 quantity in SI units
:param nml2_quantity: NeuroML2 quantity to convert
Expand Down
9 changes: 9 additions & 0 deletions test/izhikevich_test_file.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<neuroml xmlns="http://www.neuroml.org/schema/neuroml2" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.neuroml.org/schema/neuroml2 https://raw.github.com/NeuroML/NeuroML2/development/Schemas/NeuroML2/NeuroML_v2.2.xsd" id="IzhSingleNeuron">
<izhikevich2007Cell id="izh2007RS0" C="100pF" v0="-60mV" k="0.7nS_per_mV" vr="-60mV" vt="-40mV" vpeak="35mV" a="0.03per_ms" b="-2nS" c="-50.0mV" d="100pA"/>
<pulseGenerator id="pulseGen_0" delay="0ms" duration="1000ms" amplitude="0.07 nA"/>
<network id="IzhNet">
<population id="IzhPop0" component="izh2007RS0" size="1"/>
<explicitInput target="IzhPop0[0]" input="pulseGen_0"/>
</network>
</neuroml>

28 changes: 26 additions & 2 deletions test/test_pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
import os
import shutil

from pyneuroml.pynml import extract_lems_definition_files
from pyneuroml.pynml import (extract_lems_definition_files, list_exposures,
list_recording_paths)


class TestJarUtils(unittest.TestCase):
Expand All @@ -33,10 +34,33 @@ def test_lems_def_files_extraction(self):
"Synapses.xml"]

extraction_dir = extract_lems_definition_files()
newfilelist = os.listdir(extraction_dir + "/NeuroML2CoreTypes/")
newfilelist = os.listdir(extraction_dir)
assert(sorted(filelist) == sorted(newfilelist))
shutil.rmtree(extraction_dir)


class TestHelperUtils(unittest.TestCase):

"""Test helper utilities."""

def test_exposure_listing(self):
"""Test listing of exposures in NeuroML documents."""
exps = list_exposures("test/izhikevich_test_file.nml", "iz")
ctypes = {}
for key, val in exps.items():
ctypes[key.type] = val

assert ("izhikevich2007Cell" in ctypes.keys())
expnames = []
for exp in ctypes["izhikevich2007Cell"]:
expnames.append(exp.name)
assert ("u" in expnames)

def test_recording_path_listing(self):
"""Test listing of recording paths in NeuroML documents."""
paths = list_recording_paths("test/izhikevich_test_file.nml", "iz")
assert ("izh2007RS0/u" in paths)


if __name__ == "__main__":
unittest.main()

0 comments on commit 6b8ea6c

Please sign in to comment.