Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
fee5846
feat: add cell builder utilities
sanjayankur31 Jun 18, 2021
7880bfa
feat: propagate validation result to writer
sanjayankur31 Jun 18, 2021
a885a4d
feat: add cell builder utility
sanjayankur31 Jun 18, 2021
47b5cab
feat: add unit tests for cell builder utility
sanjayankur31 Jun 18, 2021
819e76d
feat: add pytest to CI
sanjayankur31 Jun 18, 2021
4e9d4db
fix: correct type hinting for list of floats
sanjayankur31 Jun 19, 2021
a5df48b
fix: use Python 2 compatible type hinting syntax
sanjayankur31 Jun 19, 2021
c4cca51
fix: drop f-strings for Python 2 compatibility
sanjayankur31 Jun 19, 2021
4b2feb8
refactor(ci): move pytest to test scripts
sanjayankur31 Jun 19, 2021
3fb3111
feat: add fraction_along to add_segment
sanjayankur31 Jun 23, 2021
107352e
feat: set default fraction_along in segment parent
sanjayankur31 Jun 23, 2021
731ddcd
feat: add summary doc string to add_segment
sanjayankur31 Jun 23, 2021
95994db
chore: remove stray doc string line
sanjayankur31 Jun 23, 2021
ec46850
feat: allow omission of proximal point in add_segment
sanjayankur31 Jun 23, 2021
3daaa24
fix: make sure p is defined even when prox is None
sanjayankur31 Jun 23, 2021
a2cca2f
feat: do not repeat includes
sanjayankur31 Jun 23, 2021
29de814
feat(cellbuilder): define a dict to store neurolex ids
sanjayankur31 Jun 24, 2021
bf5a553
feat(cellbuilder): add default segment groups
sanjayankur31 Jun 24, 2021
098df66
feat(cellbuilder): add utilities to easily find segment groups
sanjayankur31 Jun 24, 2021
04ce495
feat(cellbuilder): add newly created segments to default segments
sanjayankur31 Jun 24, 2021
91345a4
fix: correct type annotations
sanjayankur31 Jun 24, 2021
8fadf0d
chore(cellbuilder): add note about order of segment groups in NML files
sanjayankur31 Jun 24, 2021
9735a51
refactor(cellbuilder): migrate segment getters to nml.py in libNeuroML
sanjayankur31 Jun 25, 2021
ff1b0de
refactor(cellbuilder): use update nml.py methods and make conventiona…
sanjayankur31 Jun 25, 2021
b6123c4
feat(cell-builder): use py3 type annotations
sanjayankur31 Jul 26, 2022
49d687e
chore: format with black
sanjayankur31 Jul 26, 2022
ffadf98
chore(ci): remove duplicate pytest command
sanjayankur31 Jul 26, 2022
2f9eac0
feat(deps): set version constraint: libNeuroML>= 0.3.1
sanjayankur31 Jul 26, 2022
429d0a8
docs: add CellBuilder to sphinx docs
sanjayankur31 Jul 26, 2022
313c262
test(CellBuilder): re-enable skipped tests
sanjayankur31 Jul 26, 2022
8b604f5
chore: correct section heading in docs
sanjayankur31 Jul 26, 2022
8066c59
feat(cell-builder): add missing check
sanjayankur31 Jul 26, 2022
f6ba895
feat: handle exception when a segment group does not exist
sanjayankur31 Jul 27, 2022
911ed4b
test(cell-builder): add new tests
sanjayankur31 Jul 27, 2022
c3b0b89
chore: use is for comparison with None instead of ==
sanjayankur31 Jul 27, 2022
0ab7859
fix(cell-builder): append to resistivities instead of overwriting
sanjayankur31 Aug 4, 2022
311c561
fix(cell-builder): correct append function call
sanjayankur31 Aug 4, 2022
18fedd3
fix(cell-builder): append to list of InitMembPotentials
sanjayankur31 Aug 4, 2022
1ff8211
improvement(cell-builder): re-order function args
sanjayankur31 Aug 4, 2022
32d2e05
feat(cell-builder): add generic channel density helper
sanjayankur31 Aug 4, 2022
f42acf3
test(cell-builder): add test for generic conductance density adder
sanjayankur31 Aug 4, 2022
3e9b2e6
feat(cell-builder): add generic method to add membrane properties
sanjayankur31 Aug 4, 2022
fdd9e99
feat(utils): add general component factory function
sanjayankur31 Aug 4, 2022
f418f5a
improvement(utils): make component factory case sensitive
sanjayankur31 Aug 4, 2022
8cbc383
chore(utils): remove wrong doc noting names are case insensitive
sanjayankur31 Aug 4, 2022
f42dda8
test(cell-builder): correct tests
sanjayankur31 Aug 4, 2022
4b84a33
chore: add missing blank line
sanjayankur31 Aug 4, 2022
0eb1220
feat(cell-builder): add generic function to add intracellular properties
sanjayankur31 Aug 4, 2022
381e9a5
test(cell-builder): test intracellular and membrane property helpers
sanjayankur31 Aug 4, 2022
6e7ae42
test(utils): add tests for factory helper function
sanjayankur31 Aug 4, 2022
627a7ed
feat(utils): add function to check for compulsory parameters
sanjayankur31 Aug 4, 2022
a74a26c
Merge branch 'development' into feat/cell-builder-methods-105
sanjayankur31 Aug 19, 2022
be491cc
fix(component-factory): return created component
sanjayankur31 Aug 22, 2022
813217e
test(plot): disable interactive
sanjayankur31 Aug 22, 2022
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
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,10 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
pip install pytest
pip install .

- name: Run tests
run: |
pytest --cov=pyneuroml .
pynml -h

./test-ghactions.sh -neuron
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ install:
#- pip install git+https://github.com/NeuralEnsemble/pyelectro.git

- pip install --upgrade pip
- pip install flake8
- pip install flake8 pytest
- pip install .

script:
Expand Down
1 change: 1 addition & 0 deletions docs/source/pyneuroml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Subpackages
pyneuroml.swc
pyneuroml.tune
pyneuroml.neuron
pyneuroml.utils


pyneuroml.pynml module
Expand Down
16 changes: 16 additions & 0 deletions docs/source/pyneuroml.utils.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pyneuroml.utils package
==========================

.. automodule:: pyneuroml.utils
:members:
:undoc-members:
:show-inheritance:

pyneuroml.utils.CellBuilder module
---------------------------------------------

.. automodule:: pyneuroml.utils.CellBuilder
:members:
:undoc-members:
:show-inheritance:

3 changes: 1 addition & 2 deletions pyneuroml/pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"""

from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

# py3.7, 3.8 require this to use standard collections as generics
Expand Down Expand Up @@ -1092,7 +1091,7 @@ def write_neuroml2_file(
writers.NeuroMLWriter.write(nml2_doc, nml2_file_name)

if validate:
validate_neuroml2(nml2_file_name, verbose_validate)
return validate_neuroml2(nml2_file_name, verbose_validate)


def read_lems_file(
Expand Down
307 changes: 307 additions & 0 deletions pyneuroml/utils/CellBuilder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
#!/usr/bin/env python3
"""
Utility functions to help build cells in NeuroML
"""


from typing import List
from neuroml import (
Cell,
Morphology,
MembraneProperties, # type: ignore # noqa
IntracellularProperties,
BiophysicalProperties,
Segment,
SegmentGroup,
Point3DWithDiam,
SegmentParent,
Member,
InitMembPotential,
Resistivity,
SpecificCapacitance,
NeuroMLDocument,
IncludeType,
ChannelDensity,
)


neuro_lex_ids = {
"axon": "GO:0030424",
"dend": "GO:0030425",
"soma": "GO:0043025",
}


def create_cell(cell_id: str, use_convention: bool = True) -> Cell:
"""Create a NeuroML Cell.

Initialises the cell with these properties assigning IDs where applicable:
- Morphology: "morphology"
- BiophysicalProperties: "biophys"
- MembraneProperties
- IntracellularProperties

if `use_convention` is True, it also creates some default SegmentGroups for
convenience:

- "all", "soma_group", "dendrite_group", "axon_group" which
are used by other helper functions to include all, soma, dendrite, and
axon segments respectively.

Note that since this cell does not currently include a segment in its
morphology, it is *not* a valid NeuroML construct. Use the `add_segment`
function to add segments. `add_segment` will also populate the default
segment groups this creates.

:param cell_id: id of the cell
:type cell_id: str
:param use_convention: whether helper segment groups should be created using the default convention
:type use_convention: bool
:returns: created cell object of type neuroml.Cell

"""
cell = Cell(id=cell_id)
cell.morphology = Morphology(id="morphology")
membrane_properties = MembraneProperties()
intracellular_properties = IntracellularProperties()

cell.biophysical_properties = BiophysicalProperties(
id="biophys",
intracellular_properties=intracellular_properties,
membrane_properties=membrane_properties,
)

if use_convention:
seg_group_all = SegmentGroup(id="all")
seg_group_soma = SegmentGroup(
id="soma_group",
neuro_lex_id=neuro_lex_ids["soma"],
notes="Default soma segment group for the cell",
)
seg_group_axon = SegmentGroup(
id="axon_group",
neuro_lex_id=neuro_lex_ids["axon"],
notes="Default axon segment group for the cell",
)
seg_group_dend = SegmentGroup(
id="dendrite_group",
neuro_lex_id=neuro_lex_ids["dend"],
notes="Default dendrite segment group for the cell",
)
cell.morphology.segment_groups.append(seg_group_all)
cell.morphology.segment_groups.append(seg_group_soma)
cell.morphology.segment_groups.append(seg_group_axon)
cell.morphology.segment_groups.append(seg_group_dend)

return cell


def add_segment(
cell: Cell,
prox: List[float],
dist: List[float],
name: str = None,
parent: SegmentParent = None,
fraction_along: float = 1.0,
group: SegmentGroup = None,
use_convention: bool = True,
) -> Segment:
"""Add a segment to the cell.

Suggested convention: use `axon_`, `soma_`, `dend_` prefixes for axon,
soma, and dendrite segments respectivey. This will allow this function to
add the correct neurolex IDs to the group.

If `use_convention` is true, the created segment is also added to the
default segment groups that were created by the `create_cell` function:
`all`, `dendrite_group`, `soma_group`, `axon_group`. Note that while it is
not necessary to use the convention, it is necessary to be consistent.

:param cell: cell to add segment to
:type cell: Cell
:param prox: proximal segment information
:type prox: list with 4 float entries: [x, y, z, diameter]
:param dist: dist segment information
:type dist: list with 4 float entries: [x, y, z, diameter]
:param name: name of segment
:type name: str
:param parent: parent segment
:type parent: SegmentParent
:param fraction_along: where the new segment is connected to the parent (0: distal point, 1: proximal point)
:type fraction_along: float
:param group: segment group to add the segment to
:type group: SegmentGroup
:param use_convention: whether helper segment groups should be created using the default convention
:type use_convention: bool
:returns: the created segment

"""
try:
if prox:
p = Point3DWithDiam(x=prox[0], y=prox[1], z=prox[2], diameter=prox[3])
else:
p = None
except IndexError as e:
print("{}: prox must be a list of 4 elements".format(e))
try:
d = Point3DWithDiam(x=dist[0], y=dist[1], z=dist[2], diameter=dist[3])
except IndexError as e:
print("{}: dist must be a list of 4 elements".format(e))

segid = len(cell.morphology.segments)

if segid > 0 and parent is None:
raise Exception(
"There are currently more than one segments in the cell, but one is being added without specifying a parent segment"
)

sp = (
SegmentParent(segments=parent.id, fraction_along=fraction_along)
if parent
else None
)
segment = Segment(id=segid, proximal=p, distal=d, parent=sp)

if name:
segment.name = name

if group:
seg_group = None
seg_group_default = None
neuro_lex_id = None

# cell.get_segment_group throws an exception of the segment group
# does not exist
try:
seg_group = cell.get_segment_group(group)
except Exception as e:
print("Warning: {}".format(e))

if "axon_" in group:
neuro_lex_id = neuro_lex_ids[
"axon"
] # See http://amigo.geneontology.org/amigo/term/GO:0030424
if use_convention:
seg_group_default = cell.get_segment_group("axon_group")
if "soma_" in group:
neuro_lex_id = neuro_lex_ids["soma"]
if use_convention:
seg_group_default = cell.get_segment_group("soma_group")
if "dend_" in group:
neuro_lex_id = neuro_lex_ids["dend"]
if use_convention:
seg_group_default = cell.get_segment_group("dendrite_group")

if seg_group is None:
seg_group = SegmentGroup(id=group, neuro_lex_id=neuro_lex_id)
cell.morphology.segment_groups.append(seg_group)

seg_group.members.append(Member(segments=segment.id))
# Ideally, these higher level segment groups should just include other
# segment groups using Include, which would result in smaller NML
# files. However, because these default segment groups are defined
# first, they are printed in the NML file before the new segments and their
# groups. The jnml validator does not like this.
# TODO: clarify if the order of definition is important, or if the jnml
# validator needs to be updated to manage this use case.
if use_convention and seg_group_default:
seg_group_default.members.append(Member(segments=segment.id))

if use_convention:
seg_group_all = cell.get_segment_group("all")
seg_group_all.members.append(Member(segments=segment.id))

cell.morphology.segments.append(segment)
return segment


def set_init_memb_potential(cell: Cell, v: str, group: str = "all") -> None:
"""Set the initial membrane potential of the cell.

:param cell: cell to modify
:type cell: Cell
:param v: value to set for membrane potential with units
:type v: str
:param group: id of segment group to modify
:type group: str
"""
cell.biophysical_properties.membrane_properties.init_memb_potentials = [
InitMembPotential(value=v, segment_groups=group)
]


def set_resistivity(cell: Cell, resistivity: str, group: str = "all") -> None:
"""Set the resistivity of the cell

:param cell: cell to modfify
:param resistivity: value resistivity to set with units
:type resistivity: str
:param group: segment group to modify
:type group: str
"""
cell.biophysical_properties.intracellular_properties.resistivities = [
Resistivity(value=resistivity, segment_groups=group)
]


def set_specific_capacitance(cell: Cell, spec_cap: str, group: str = "all") -> None:
"""Set the specific capacitance for the cell.

:param cell: cell to set specific capacitance for
:type cell: Cell
:param spec_cap: value of specific capacitance with units
:type spec_cap: str
:param group: segment group to modify
:type group: str
"""
cell.biophysical_properties.membrane_properties.specific_capacitances.append(
SpecificCapacitance(value=spec_cap, segment_groups=group)
)


def add_channel_density(
cell: Cell,
nml_cell_doc: NeuroMLDocument,
cd_id: str,
cond_density: str,
ion_channel: str,
ion_chan_def_file: str = "",
erev: str = "0.0 mV",
ion: str = "non_specific",
group: str = "all",
) -> None:
"""Add channel density.

:param cell: cell to be modified
:type cell: Cell
:param nml_cell_doc: cell NeuroML document to which channel density is to be added
:type nml_cell_doc: NeuroMLDocument
:param cd_id: id for channel density
:type cd_id: str
:param cond_density: value of conductance density with units
:type cond_density: str
:param ion_channel: name of ion channel
:type ion_channel: str
:param ion_chan_def_file: path to NeuroML2 file defining the ion channel, if empty, it assumes the channel is defined in the same file
:type ion_chan_def_file: str
:param erev: value of reversal potential with units
:type erev: str
:param ion: name of ion
:type ion: str
:param group: segment groups to add to
:type group: str
"""
cd = ChannelDensity(
id=cd_id,
segment_groups=group,
ion=ion,
ion_channel=ion_channel,
erev=erev,
cond_density=cond_density,
)

cell.biophysical_properties.membrane_properties.channel_densities.append(cd)

if len(ion_chan_def_file) > 0:
if IncludeType(ion_chan_def_file) not in nml_cell_doc.includes:
nml_cell_doc.includes.append(IncludeType(ion_chan_def_file))
8 changes: 8 additions & 0 deletions pyneuroml/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python3
"""
The utils package contains various utility functions to aid users working with
PyNeuroML

Copyright 2021 NeuroML Contributors
Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
"""
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"pylems>=0.5.7",
"airspeed>=0.5.5",
"neuromllite>=0.4.1", # sets dependency for libNeuroML also
"libNeuroML>=0.3.1",
"matplotlib",
"graphviz",
'typing; python_version<"3.5"',
Expand Down
Loading