Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2eb3106
feat(pynml): add function to extract LEMS NeuroML2 definitions
sanjayankur31 Jul 20, 2021
b0c2bf9
ci: enable unit testing with pytest
sanjayankur31 Jul 20, 2021
30e9a0b
ci(flake8): move flake to run at end of CI cycle
sanjayankur31 Jul 20, 2021
ded6f11
chore(logging): set default level to INFO
sanjayankur31 Jul 20, 2021
8026caf
refactor(pynml): simplify function to only return str path of dir
sanjayankur31 Jul 21, 2021
fbe811b
refactor(pynml): improve LEMS definition file extraction function
sanjayankur31 Jul 21, 2021
7e85e40
feat(pynml): add functions to list exposures and list recording paths
sanjayankur31 Jul 21, 2021
0481af1
ci: add requirements file for development
sanjayankur31 Jul 21, 2021
0c40ff8
improvement(pynml): set the default logging level to INFO
sanjayankur31 Jul 21, 2021
a072bc9
improvement(tests): correct removal of temp dir
sanjayankur31 Jul 21, 2021
82931e8
improvement(tests): set default logging level for test
sanjayankur31 Jul 21, 2021
cc468ac
improvement(tests): add more assertions to test
sanjayankur31 Jul 22, 2021
c63c146
chore(tests): consolidate in one folder
sanjayankur31 Aug 4, 2021
4a300f5
chore: remove duplicate requirements file
sanjayankur31 Aug 4, 2021
e929f12
feat: update function name
sanjayankur31 Aug 10, 2021
056481b
test(pynml): update test for renamed function
sanjayankur31 Aug 10, 2021
cb28c66
feat: update functions to match improved pylems functions
sanjayankur31 Aug 16, 2021
5600d55
test: add detailed HH neuron example as test for exposure path listing
sanjayankur31 Aug 16, 2021
343f70e
chore(deps): bump pylems minimum version to 0.5.7
sanjayankur31 Aug 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
pip install pytest
pip install .

- name: Lint with flake8
run: |
pip install flake8
flake8 . --count --exit-zero --show-source --max-line-length=127 --statistics
- name: Run tests
run: |
pytest .
pynml -h
./test-ghactions.sh
- name: Lint with flake8
run: |
pip install flake8
flake8 . --count --exit-zero --show-source --max-line-length=127 --statistics
4 changes: 2 additions & 2 deletions pyneuroml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
# Define a logger for the package
logging.basicConfig(format="pyNeuroML >>> %(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.WARN)
logger = logging.getLogger(__name__)
logger.setLevel(logging.WARN)
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setLevel(logging.INFO)
formatter = logging.Formatter('pyNeuroML >>> %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
129 changes: 128 additions & 1 deletion pyneuroml/pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from __future__ import print_function
from __future__ import unicode_literals
import os
import shutil
import sys
import subprocess
import math
Expand All @@ -25,6 +26,7 @@
from lxml import etree
import pprint
import logging
import tempfile

try:
import typing
Expand All @@ -33,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 All @@ -51,6 +54,7 @@
lems_model_with_units = None

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


def parse_arguments():
Expand Down Expand Up @@ -317,6 +321,129 @@ def get_lems_model_with_units():
return lems_model_with_units


def extract_lems_definition_files(path=None):
# type: (typing.Union[str, None, tempfile.TemporaryDirectory[typing.Any]]) -> str
"""Extract the NeuroML2 LEMS definition files to a directory and return its path.

This function can be used by other LEMS related functions that need to
include the NeuroML2 LEMS definitions.

If a path is provided, the folder is created relative to the current
working directory.

If no path is provided, for repeated usage for example, the files are
extracted to a temporary directory using Python's
`tempfile.mkdtemp
<https://docs.python.org/3/library/tempfile.html>`__ function.

Note: in both cases, it is the user's responsibility to remove the created
directory when it is no longer required, for example using. the
`shutil.rmtree()` Python function.

:param path: path of directory relative to current working directory to extract to, or None
:type path: str or None
:returns: directory path
"""
jar_path = get_path_to_jnml_jar()
logger.debug("Loading standard NeuroML2 dimension/unit definitions from %s" % jar_path)
jar = zipfile.ZipFile(jar_path, 'r')
namelist = [x for x in jar.namelist() if ".xml" in x and "NeuroML2CoreTypes" in x]
logger.debug("NeuroML LEMS definition files in jar are: {}".format(namelist))

# If a string is provided, ensure that it is relative to cwd
if path and isinstance(path, str) and len(path) > 0:
path = "./" + path
try:
os.makedirs(path)
except FileExistsError:
logger.warn("{} already exists. Any NeuroML LEMS files in it will be overwritten".format(path))
except OSError as err:
logger.critical(err)
sys.exit(-1)
else:
path = tempfile.mkdtemp()

logger.debug("Created directory: " + path)
jar.extractall(path, namelist)
path = path + "/NeuroML2CoreTypes/"
logger.info("NeuroML LEMS definition files extracted to: {}".format(path))
return path


def list_exposures(nml_doc_fn, substring=""):
# 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_for_exposures(nml_doc_fn, substring="", target=""):
# type: (str, 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_for_exposures(substring, target)


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)
if logger.level < logging.INFO:
new_lems_model.debug = True
else:
new_lems_model.debug = False
neuroml2_defs_dir = extract_lems_definition_files()
filelist = os.listdir(neuroml2_defs_dir)
# Remove the temporary directory
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[:-1 * len("NeuroML2CoreTypes/")])
return new_lems_model


def split_nml2_quantity(nml2_quantity):
# type: (str) -> typing.Tuple[float, str]
"""Split a NeuroML 2 quantity into its magnitude and units
Expand All @@ -340,7 +467,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
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
long_description_content_type="text/markdown",
install_requires=[
'argparse',
'pylems>=0.5.0',
'pylems>=0.5.7',
'airspeed>=0.5.5',
'libNeuroML>=0.2.52',
'neuromllite>=0.2.2',
Expand Down
28 changes: 28 additions & 0 deletions tests/HH_example_cell.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<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="cell">
<notes>HH cell</notes>
<include href="HH_example_na_channel.nml"/>
<include href="HH_example_k_channel.nml"/>
<include href="HH_example_leak_channel.nml"/>
<cell id="hh_cell">
<notes>A single compartment HH cell</notes>
<morphology id="hh_cell_morph">
<segment id="0" name="soma">
<proximal x="0.000000e+00" y="0.000000e+00" z="0.000000e+00" diameter="17.841241161527712"/>
<distal x="0.000000e+00" y="0.000000e+00" z="0.000000e+00" diameter="17.841241161527712"/>
</segment>
</morphology>
<biophysicalProperties>
<membraneProperties>
<channelDensity id="na_channels" ionChannel="na_channel" condDensity="120.0 mS_per_cm2" erev="50.0 mV" ion="na"/>
<channelDensity id="k_channels" ionChannel="k_channel" condDensity="360 S_per_m2" erev="-77mV" ion="k"/>
<channelDensity id="leak_channels" ionChannel="leak_channel" condDensity="3.0 S_per_m2" erev="-54.3mV" ion="non_specific"/>
<spikeThresh value="-20mV"/>
<specificCapacitance value="1.0 uF_per_cm2"/>
<initMembPotential value="-65mV"/>
</membraneProperties>
<intracellularProperties>
<resistivity value="0.03 kohm_cm"/>
</intracellularProperties>
</biophysicalProperties>
</cell>
</neuroml>
11 changes: 11 additions & 0 deletions tests/HH_example_k_channel.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<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="k_channel">
<notes>k channel for HH neuron</notes>
<ionChannelHH id="k_channel" species="k" conductance="10pS">
<notes>Potassium channel for HH cell</notes>
<gateHHrates id="k_n" instances="4">
<notes>n gate for k channel</notes>
<forwardRate type="HHExpLinearRate" rate="0.1per_ms" midpoint="-55mV" scale="10mV"/>
<reverseRate type="HHExpRate" rate="0.125per_ms" midpoint="-65mV" scale="-80mV"/>
</gateHHrates>
</ionChannelHH>
</neuroml>
6 changes: 6 additions & 0 deletions tests/HH_example_leak_channel.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<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="leak_channel">
<notes>leak channel for HH neuron</notes>
<ionChannelHH id="leak_channel" conductance="10pS">
<notes>Leak conductance</notes>
</ionChannelHH>
</neuroml>
16 changes: 16 additions & 0 deletions tests/HH_example_na_channel.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<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="na_channel">
<notes>Na channel for HH neuron</notes>
<ionChannelHH id="na_channel" species="na" conductance="10pS">
<notes>Sodium channel for HH cell</notes>
<gateHHrates id="na_m" instances="3">
<notes>m gate for na channel</notes>
<forwardRate type="HHExpLinearRate" rate="1per_ms" midpoint="-40mV" scale="10mV"/>
<reverseRate type="HHExpRate" rate="4per_ms" midpoint="-65mV" scale="-18mV"/>
</gateHHrates>
<gateHHrates id="na_h" instances="1">
<notes>h gate for na channel</notes>
<forwardRate type="HHExpRate" rate="0.07per_ms" midpoint="-65mV" scale="-20mV"/>
<reverseRate type="HHSigmoidRate" rate="1per_ms" midpoint="-35mV" scale="10mV"/>
</gateHHrates>
</ionChannelHH>
</neuroml>
22 changes: 22 additions & 0 deletions tests/HH_example_net.nml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<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="network">
<notes>HH cell network</notes>
<include href="HH_example_cell.nml"/>
<pulseGenerator id="pg" delay="100ms" duration="100ms" amplitude="0.08nA">
<notes>Simple pulse generator</notes>
</pulseGenerator>
<network id="single_hh_cell_network">
<population id="pop0" component="hh_cell" size="2">
<notes>A population for our cell</notes>
</population>
<populationList id="pop1" component="hh_cell">
<notes>A populationList for our cell</notes>
<instance id="0">
<location x="0" y="0" z="0" />
</instance>
<instance id="1">
<location x="0" y="0" z="0" />
</instance>
</populationList>
<explicitInput target="pop0[0]" input="pg"/>
</network>
</neuroml>
9 changes: 9 additions & 0 deletions tests/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>

Loading