Skip to content

Commit

Permalink
Updated neuroml2 imports with error messages (#472)
Browse files Browse the repository at this point in the history
* Fixed showmsg() bug in pybind11 based version.

The newer implementation of showmsg() shows the outgoing messages with
misdirected arrows. Fixed now.

* Added newer version of pybind11

* Swapped clocks for channel and compartment to fix  #467

* This actually fixes scheduling for channels and compartments #467

The previous commit only updated the docs. This one modifies the
actual tick assignments.
Now electrical compartments are scheduled on ticks 2 and 3 and
channels (all derived from ChanBase) on 4.

* Minor fix for compiler warning on loop var creating copy from const

* Removed CMake generated compile command.

the compile command file is generated by cmake in each build
environment and should not be part of the repo.

* Added error message at neuroml import error, corrected imports.

* Updated neuroml2 reader. Yet to debug Granule_98 model.

* Updated neuroml2 reader to handle spherical compartment for CaPool

Also created separate test script for Granule98 model.

* Fixed deprecated imp module use in rdesigneur.

- rdesigneur used imp module, now replaced by importlib.
- commented out debug logs from neuroml2 reader
- TODO: tests/support has files which conflict with tests/core. These
  seem to be old test code which may better be removed.

* Updated test script for Granule98 model

---------

Co-authored-by: HarshaRani <ranishashi@gmail.com>
  • Loading branch information
subhacom and hrani committed Apr 11, 2024
1 parent 5639f3e commit 8939ede
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 49 deletions.
2 changes: 1 addition & 1 deletion python/moose/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ def doc(arg, paged=True):
"""
text = _moose.__generatedoc__(arg)
if pydoc.page:
if pydoc.pager:
pydoc.pager(text)
else:
print(text)
Expand Down
48 changes: 34 additions & 14 deletions python/moose/neuroml2/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,26 @@

import os
import math
import logging
import numpy as np
import moose

import logging

logger_ = logging.getLogger('moose.nml2')

import neuroml as nml
import pyneuroml.pynml as pynml

nml_not_available_msg = ''

try:
import neuroml as nml
import pyneuroml.pynml as pynml
except ImportError as error:
raise ImportError(f'Could not import neuroml/pyneuroml. Please make sure you have pyneuroml installed (`pip install pyneuroml`)') from error


from moose.neuroml2.units import SI


def _write_flattened_nml( doc, outfile ):
"""_write_flattened_nml
Concat all NML2 read by moose and generate one flattened NML file.
Expand Down Expand Up @@ -68,14 +78,15 @@ def _unique( ls ):

def _isConcDep(ct):
"""_isConcDep
Check if componet is dependant on concentration. Most HHGates are
dependant on voltage.
Check if componet is dependent on concentration. Most HHGates are
dependent on voltage.
:param ct: ComponentType
:type ct: nml.ComponentType
:return: True if Component is depenant on conc, False otherwise.
"""
# logger_.debug(f"{'#' * 10} EXTENDS {ct.extends}")
if 'ConcDep' in ct.extends:
return True
return False
Expand Down Expand Up @@ -209,6 +220,7 @@ def read(self, filename, symmetric=True):
self.importInputs(self.doc)

for cell in self.doc.cells:
# logger_.debug(f"{'%' * 10} Creating cell prototype {cell}")
self.createCellPrototype(cell, symmetric=symmetric)

if len(self.doc.networks)>=1:
Expand Down Expand Up @@ -427,10 +439,15 @@ def copySpecies(self, species, compartment):
raise RuntimeError(msg)
pool_id = moose.copy(proto_pool, compartment, species.id)
pool = moose.element(pool_id)
pool.B = pool.B / (np.pi * compartment.length * (
# print('&' * 10, compartment.path, compartment.length, compartment.diameter, pool.thick)
if compartment.length <= 0:
vol = 4 * np.pi * (0.5 * compartment.diameter**3 - (0.5 * compartment.diameter - pool.thick)**3) / 3
else:
vol = (np.pi * compartment.length * (
0.5 * compartment.diameter + pool.thick) *
(0.5 * compartment.diameter - pool.thick)
)
pool.B = pool.B / vol
return pool

def importAxialResistance(self, nmlcell, intracellularProperties):
Expand Down Expand Up @@ -475,18 +492,20 @@ def calculateRateFn(self, ratefn, vmin, vmax, tablen=3000, vShift='0mV'):
if ratefn.type != ct.name:
continue

logger_.info("Using %s to evaluate rate"%ct.name)
logger_.info(f"Using %s to evaluate rate"%ct.name)
rate = []
for v in tab:
# Note: MOOSE HHGate are either voltage of concentration
# dependant. Here we figure out if nml description of gate is
# concentration dependant or note.
# Note: MOOSE HHGate are voltage and/or concentration
# dependent. Here we figure out if nml description of gate is
# concentration dependent or not.
# logger_.debug(f"{'#' * 5} {_isConcDep(ct)}")
if _isConcDep(ct):
# Concentration dependant. Concentration can't be negative.
# Concentration dependent. Concentration can't be negative.
# Find a suitable CaConc from the /library. Currently on Ca
# dependant channels are allowed.
# dependent channels are allowed.
caConcName = _findCaConcVariableName()
req_vars = {caConcName:'%g'%max(0,v),'vShift':vShift,'temperature':self._getTemperature()}
req_vars = {'v': '0.0V', 'caConc':f'{max(1e-11,v):g}', caConcName:f'{max(1e-11,v):g}','vShift':vShift,'temperature':self._getTemperature()}
# logger_.debug(f"{'A' * 30} {req_vars}")
else:
req_vars = {'v':'%sV'%v,'vShift':vShift,'temperature':self._getTemperature()}
req_vars.update( self._variables )
Expand Down Expand Up @@ -669,6 +688,7 @@ def importInputs(self, doc):
pg.firstWidth = SI(pg_nml.duration)
pg.firstLevel = SI(pg_nml.amplitude)
pg.secondDelay = 1e9
logger_.debug(f'{"$" * 10} Created input {epath}')


def importIonChannels(self, doc, vmin=-150e-3, vmax=100e-3, vdivs=5000):
Expand Down Expand Up @@ -707,6 +727,6 @@ def createDecayingPoolConcentrationModel(self, concModel):
# shell and d is thickness - must divide by
# shell volume when copying
self.proto_pools[concModel.id] = ca
self.nml_concs_to_moose[concModel.id] = ca
self.nml_conc_to_moose[concModel.id] = ca
self.moose_to_nml[ca] = concModel
logger_.debug('Created moose element: %s for nml conc %s' % (ca.path, concModel.id))
65 changes: 65 additions & 0 deletions python/moose/neuroml2/test_granule98.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# test_granule98.py ---
#
# Filename: test_granule98.py
# Description:
# Author: Subhasis Ray
# Created: Mon Apr 8 21:41:22 2024 (+0530)
# Last-Updated: Wed Apr 10 19:59:48 2024 (+0530)
# By: Subhasis Ray
#

# Code:
"""Test code for the Granule cell model
"""
import os
import numpy as np
# import unittest
import logging


LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO')
logging.basicConfig(level=LOGLEVEL)


import moose
from moose.neuroml2.reader import NML2Reader


def run(nogui=True):
reader = NML2Reader()
filename = 'test_files/Granule_98/GranuleCell.net.nml'
reader.read(filename)
soma = reader.getComp(reader.doc.networks[0].populations[0].id, 0, 0)
data = moose.Neutral('/data')
pg = reader.getInput('Gran_10pA')
inj = moose.Table(f'{data.path}/pulse')
moose.connect(inj, 'requestOut', pg, 'getOutputValue')
vm = moose.Table(f'{data.path}/Vm')
moose.connect(vm, 'requestOut', soma, 'getVm')
print('A' * 10, soma)

simtime = 300e-3
moose.reinit()
moose.start(simtime)

t = np.arange(len(vm.vector)) * vm.dt
print('%' * 10, len(vm.vector), len(inj.vector))
results = np.array([t, vm.vector, inj.vector], dtype=[('time', float), ('Vm', float), ('Im', float)])
fname = 'Granule_98.npy'
np.save(fname, results)
print(f'Saved results in {fname}')
if not nogui:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, sharex='all')
axes[0].plot(t, vm.vector, label='Vm')
axes[1].plot(t, inj.vector, label='Im')
plt.legend()
plt.show()


run(nogui=False)


#
# test_granule98.py ends here
37 changes: 12 additions & 25 deletions python/moose/neuroml2/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
# Maintainer:
# Created: Wed Jul 24 16:02:21 2013 (+0530)
# Version:
# Last-Updated: Sun Apr 17 16:13:01 2016 (-0400)
# By: subha
# Update #: 112
# Last-Updated: Mon Apr 8 21:41:42 2024 (+0530)
# By: Subhasis Ray
# Update #: 125
# URL:
# Keywords:
# Compatibility:
Expand Down Expand Up @@ -46,13 +46,18 @@

# Code:

from __future__ import print_function
import os
import unittest
import numpy as np
import logging


LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO')
logging.basicConfig(level=LOGLEVEL)


import moose
import neuroml as nml
from reader import NML2Reader
import os
from moose.neuroml2.reader import NML2Reader

class TestFullCell(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -125,24 +130,6 @@ def test_HHChannels(self):
self.assertTrue(len(chans) < 3) # Only soma and dendrite2 have the channels
self.assertAlmostEqual(soma_na.Gbar, 120e-2 * self.soma.diameter * self.soma.diameter * np.pi, places=6)

'''
Not yet working in NML2...
class TestGran98(unittest.TestCase):
def setUp(self):
self.reader = NML2Reader()
self.lib = moose.Neutral('/library')
self.filename = 'test_files/Granule_98/Granule_98.nml'
self.reader.read(self.filename)
for ncell in self.reader.nml_to_moose:
if isinstance(ncell, nml.Cell):
self.ncell = ncell
break
self.mcell = moose.element(moose.wildcardFind('/##[ISA=Cell]')[0])
def test_CaPool(self):
pass
'''

if __name__ == '__main__':
unittest.main()
Expand Down
14 changes: 11 additions & 3 deletions python/moose/neuroml2/test_reader2.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,19 @@
# Code:

from __future__ import print_function

import os
import unittest
import logging


LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO')
logging.basicConfig(level=LOGLEVEL)


import moose
from reader import NML2Reader
import neuroml as nml
import os
from moose.neuroml2.reader import NML2Reader


class TestPassiveCell(unittest.TestCase):
def setUp(self):
Expand Down
14 changes: 8 additions & 6 deletions python/rdesigneur/rdesigneur.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@
## latter in the former, including mapping entities like calcium and
## channel conductances, between them.
##########################################################################
from __future__ import print_function, absolute_import, division

# FIXME: Deprecated since 3.4
import imp
import importlib
import os
import moose
import numpy as np
Expand Down Expand Up @@ -323,14 +321,18 @@ def buildProtoFromFunction( self, func, protoName ):
modulePath = os.path.realpath(os.path.join(*pathTokens[:-1]))
moduleName = pathTokens[-1]
funcName = func[modPos+1:bracePos]
moduleFile, pathName, description = imp.find_module(moduleName, [modulePath])
# moduleFile, pathName, description = imp.find_module(moduleName, [modulePath])
# `imp` has been deprecated and throws error in Python 3.12
spec = importlib.machinery.PathFinder().find_spec(moduleName, [modulePath])
try:
module = imp.load_module(moduleName, moduleFile, pathName, description)
# module = imp.load_module(moduleName, moduleFile, pathName, description)
module = importlib.util.module_from_spec(spec)
funcObj = getattr(module, funcName)
funcObj(protoName)
return True
finally:
moduleFile.close()
pass
# moduleFile.close()
return False
if not func[0:bracePos] in globals():
raise BuildError( \
Expand Down

0 comments on commit 8939ede

Please sign in to comment.