Skip to content

Commit 20992e7

Browse files
authored
Merge pull request #193 from NeuroML/feat/document-convert-mod-to-neuroml
Move mod2nml script to neuron utilities
2 parents 5c57be3 + c55e4b3 commit 20992e7

File tree

8 files changed

+196
-154
lines changed

8 files changed

+196
-154
lines changed
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
<?xml version="1.0" encoding="ISO-8859-1"?>
2-
<neuroml xmlns="http://www.neuroml.org/schema/neuroml2"
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xsi:schemaLocation="http://www.neuroml.org/schema/neuroml2 https://raw.github.com/NeuroML/NeuroML2/development/Schemas/NeuroML2/NeuroML_v2beta4.xsd"
2+
<neuroml xmlns="http://www.neuroml.org/schema/neuroml2"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://www.neuroml.org/schema/neuroml2 https://raw.github.com/NeuroML/NeuroML2/development/Schemas/NeuroML2/NeuroML_v2.3.xsd"
55
id="${id}">
66

77
<notes>NeuroML file automatically generated from an NMODL file</notes>
88

99
<ionChannel id="${id}" conductance="10pS" type="${type}" species="${species}">
10-
10+
1111
<notes>${notes}</notes>
12-
12+
1313
#foreach ($gate in $gates)##
1414

1515
<gate id="${gate.id}" type="${gate.type}" instances="${gate.instances}">
16-
16+
1717
</gate>
18-
#end##
19-
18+
#end##
19+
2020
</ionChannel>
21-
21+
2222
</neuroml>

pyneuroml/neuron/__init__.py

Lines changed: 140 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
import pathlib
1414
import json
1515
import math
16-
import re
17-
18-
16+
import pprint
17+
import airspeed
1918
import yaml
2019

2120
try:
@@ -30,6 +29,7 @@
3029
from neuron import h
3130

3231

32+
pp = pprint.PrettyPrinter(depth=4)
3333
logger = logging.getLogger(__name__)
3434
logger.setLevel(logging.INFO)
3535

@@ -469,7 +469,9 @@ def getinfo(seclist: list, doprint: str = "", listall: bool = False):
469469
not replace_brackets(str(sec))
470470
in paramsectiondict[rm_NML_str(pname[0])].keys()
471471
):
472-
paramsectiondict[rm_NML_str(pname[0])][replace_brackets(str(sec))] = {
472+
paramsectiondict[rm_NML_str(pname[0])][
473+
replace_brackets(str(sec))
474+
] = {
473475
"id": str(sec),
474476
}
475477
if rm_NML_str(pname[0]) in seginfo[seg]:
@@ -488,13 +490,13 @@ def getinfo(seclist: list, doprint: str = "", listall: bool = False):
488490
unique_values = list(set(values))
489491
# if all values are the same, only print them once as '*'
490492
if len(unique_values) == 1:
491-
paramsectiondict[rm_NML_str(pname[0])][replace_brackets(str(sec))].update(
492-
{"nseg": sec.nseg, "values": {"*": unique_values[0]}}
493-
)
493+
paramsectiondict[rm_NML_str(pname[0])][
494+
replace_brackets(str(sec))
495+
].update({"nseg": sec.nseg, "values": {"*": unique_values[0]}})
494496
else:
495-
paramsectiondict[rm_NML_str(pname[0])][replace_brackets(str(sec))].update(
496-
{"nseg": sec.nseg, "values": newseginfo}
497-
)
497+
paramsectiondict[rm_NML_str(pname[0])][
498+
replace_brackets(str(sec))
499+
].update({"nseg": sec.nseg, "values": newseginfo})
498500

499501
if listall or numSecPresent > 0:
500502
mt_dict = {
@@ -724,3 +726,131 @@ def rm_NML_str(astring: str) -> str:
724726
if "_NML2" in astring:
725727
logger.info(f"Removing '_NML2' string from {astring} to ease comparison")
726728
return astring.replace("_NML2", "")
729+
730+
731+
def export_mod_to_neuroml2(mod_file: str):
732+
"""Helper function to export a mod file describing an ion channel to
733+
NeuroML2 format.
734+
735+
Note that these exports usually require more manual work before they are
736+
the converstion is complete. This method tries to take as much information
737+
as it can from the mod file to convert it into a NeuroML2 channel file.
738+
739+
Please use `pynml-channelanalysis` and `pynml-modchannelanalysis` commands
740+
to generate steady state etc. plots for the two implementations to compare
741+
them.
742+
743+
See also: https://docs.neuroml.org/Userdocs/CreatingNeuroMLModels.html#b-convert-channels-to-neuroml
744+
745+
:param mod_file: full path to mod file
746+
:type mod_file: str
747+
"""
748+
logger.info("Generating NeuroML2 representation for mod file: " + mod_file)
749+
750+
blocks = {}
751+
info = {}
752+
lines = [(str(ll.strip())).replace("\t", " ") for ll in open(mod_file)]
753+
line_num = 0
754+
while line_num < len(lines):
755+
l = lines[line_num]
756+
if len(l) > 0:
757+
logger.info(">>> %i > %s" % (line_num, l))
758+
# @type l str
759+
if l.startswith("TITLE"):
760+
blocks["TITLE"] = l[6:].strip()
761+
if "{" in l:
762+
block_name = l[: l.index("{")].strip()
763+
blocks[block_name] = []
764+
765+
li = l[l.index("{") + 1 :]
766+
bracket_depth = __check_brackets(li, 1)
767+
while bracket_depth > 0:
768+
if len(li) > 0:
769+
blocks[block_name].append(li)
770+
logger.info(" > %s > %s" % (block_name, li))
771+
line_num += 1
772+
li = lines[line_num]
773+
774+
bracket_depth = __check_brackets(li, bracket_depth)
775+
776+
rem = li[:-1].strip()
777+
if len(rem) > 0:
778+
blocks[block_name].append(rem)
779+
780+
line_num += 1
781+
782+
for line in blocks["STATE"]:
783+
if " " in line or "\t" in line:
784+
blocks["STATE"].remove(line)
785+
for s in line.split():
786+
blocks["STATE"].append(s)
787+
788+
for line in blocks["NEURON"]:
789+
if line.startswith("SUFFIX"):
790+
info["id"] = line[7:].strip()
791+
if line.startswith("USEION") and "WRITE" in line:
792+
info["species"] = line.split()[1]
793+
794+
gates = []
795+
for s in blocks["STATE"]:
796+
gate = {}
797+
gate["id"] = s
798+
gate["type"] = "???"
799+
gate["instances"] = "???"
800+
gates.append(gate)
801+
802+
info["type"] = "ionChannelHH"
803+
info["gates"] = gates
804+
805+
info["notes"] = (
806+
"NeuroML2 file automatically generated from NMODL file: %s" % mod_file
807+
)
808+
809+
pp.pprint(blocks)
810+
811+
chan_file_name = "%s.channel.nml" % info["id"]
812+
chan_file = open(chan_file_name, "w")
813+
chan_file.write(__merge_with_template(info))
814+
chan_file.close()
815+
816+
817+
def __check_brackets(line, bracket_depth):
818+
"""Check matching brackets
819+
820+
:param line: line to check
821+
:type line: str
822+
:param bracket_depth: current depth/level of brackets
823+
:type bracket_depth: int
824+
:returns: new bracket depth
825+
:rtype: int
826+
"""
827+
if len(line) > 0:
828+
bracket_depth0 = bracket_depth
829+
for c in line:
830+
if c == "{":
831+
bracket_depth += 1
832+
elif c == "}":
833+
bracket_depth -= 1
834+
if bracket_depth0 != bracket_depth:
835+
logger.info(
836+
" <%s> moved bracket %i -> %i"
837+
% (line, bracket_depth0, bracket_depth)
838+
)
839+
return bracket_depth
840+
841+
842+
def __merge_with_template(info):
843+
"""Merge information with the airspeed template file
844+
845+
:param info: information to fill in the template
846+
:type info:
847+
:returns: filled template
848+
:rtype: str
849+
"""
850+
templfile = "TEMPLATE.channel.nml"
851+
if not os.path.isfile(templfile):
852+
templfile = os.path.join(os.path.dirname(__file__), templfile)
853+
logger.info("Merging with template %s" % templfile)
854+
with open(templfile) as f:
855+
templ = airspeed.Template(f.read())
856+
return templ.merge(info)

pyneuroml/neuron/analysis/HHanalyse.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
#!/usr/bin/env python
22

3-
#
4-
#
5-
# A file which can be run in (Python enabled) NEURON to analyse the rate
6-
# variables contained in a mod file
7-
#
8-
#
3+
"""
4+
Implementation of the pynml-modchananalysis command
5+
"""
6+
7+
# TODO: clean up and refactor to allow usage from Python API
98

109
import argparse
1110
import re
1211
from math import log
1312
import neuron
14-
15-
print("\n\n")
16-
1713
import matplotlib.pyplot as pylab
1814
from pylab import *
19-
2015
from pyneuroml.analysis.NML2ChannelAnalysis import get_state_color
2116

2217

18+
print("\n\n")
19+
20+
2321
def process_args():
2422
"""
2523
Parse command-line arguments.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""
2+
Utilities to analyse NEURON models.
3+
"""

pyneuroml/neuron/mod/README.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

pyneuroml/neuron/mod/parse_mod_file.py

Lines changed: 0 additions & 111 deletions
This file was deleted.

0 commit comments

Comments
 (0)