Skip to content

Commit

Permalink
Add test from nrntest using mcna.mod (#2308)
Browse files Browse the repository at this point in the history
* Allow using coreneuron as a context manager.
* Reorganise tests a little to accommodate tests that
  are not compatible with CoreNEURON.
* nrntest: update 'submodule'.
  • Loading branch information
olupton authored Jun 23, 2023
1 parent a85f6ee commit e029076
Show file tree
Hide file tree
Showing 38 changed files with 915 additions and 164 deletions.
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,6 @@ if(NRN_ENABLE_CORENEURON)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/coreneuron
${PROJECT_SOURCE_DIR}/cmake/coreneuron/packages)

set(nrn_using_ext_corenrn FALSE)
# If NEURON tests are enabled then enable CoreNEURON tests too
set(CORENRN_ENABLE_UNIT_TESTS
${NRN_ENABLE_TESTS}
Expand Down
49 changes: 49 additions & 0 deletions share/lib/python/neuron/coreneuron.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import sys


class CoreNEURONContextHelper(object):
def __init__(self, coreneuron, new_values):
self._coreneuron = coreneuron
self._new_values = new_values
self._old_values = None

def __enter__(self):
assert self._new_values is not None
assert self._old_values is None
self._old_values = {}
for k, v in self._new_values.items():
self._old_values[k] = getattr(self._coreneuron, k)
setattr(self._coreneuron, k, v)

def __exit__(self, exc_type, exc_val, exc_tb):
assert self._new_values is not None
assert self._old_values is not None
assert self._new_values.keys() == self._old_values.keys()
# Make sure we restore values in reverse order to how we set them.
# This is important for pairs like gpu and cell_permute that interact.
for k in reversed(self._new_values.keys()):
assert getattr(self._coreneuron, k) == self._new_values[k]
setattr(self._coreneuron, k, self._old_values[k])
return False


class coreneuron(object):
"""
CoreNEURON configuration values.
Expand All @@ -9,6 +35,9 @@ class coreneuron(object):
a class instead of a module allows getter/setter methods to be used, which
lets us ensure its properties have consistent types and values.
This can also be used as a context manager to change CoreNEURON settings
only inside a particular scope.
Attributes
----------
cell_permute
Expand All @@ -25,6 +54,12 @@ class coreneuron(object):
>>> coreneuron.enable = True
>>> coreneuron.enable
True
>>> coreneuron.enable = False
>>> with coreneuron(enable=True):
... assert coreneuron.enable
... coreneuron.enable
False
"""

def __init__(self):
Expand All @@ -38,6 +73,20 @@ def __init__(self):
self._prcellstate = -1
self._model_stats = False

def __call__(self, **kwargs):
"""
Yields a context manager helper that can be used in a with statement.
This allows the syntax
with coreneuron(foo=bar):
assert coreneuron.foo == bar
assert coreneuron.foo == old_value
Discarding the return value, or using it in any way other than in a
with statement, will have no effect.
"""
return CoreNEURONContextHelper(self, kwargs)

def _default_cell_permute(self):
return 1 if self._gpu else 0

Expand Down
13 changes: 13 additions & 0 deletions share/lib/python/neuron/tests/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ def cvode_use_long_double(cv, enabled):
cv.use_long_double(old_setting)


@contextmanager
def fast_imem(enabled):
from neuron import h

cvode = h.CVode()
old_setting = cvode.use_fast_imem()
cvode.use_fast_imem(enabled)
try:
yield None
finally:
cvode.use_fast_imem(old_setting)


@contextmanager
def hh_table_disabled():
"""
Expand Down
64 changes: 36 additions & 28 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -205,21 +205,30 @@ if(NRN_ENABLE_PYTHON)
list(APPEND pytest_args --cov-report=xml --cov=neuron)
endif()
set(pytest -m pytest ${pytest_args})
# TODO: consider allowing the group-related parts to be dropped here
# In brief:
#
# * tests and mod files under pytest/ are only compatible with NEURON
# * tests and mod files under pytest_coreneuron/ are compatible with both NEURON and CoreNEURON
#
# So, pytest/ should generally be viewed as a staging area en route to pytest_coreneuron. As the
# name implies, tests in these directories are executed using pytest.
nrn_add_test_group(NAME pytest MODFILE_PATTERNS test/pytest/*.mod)
nrn_add_test_group(
CORENEURON
NAME pynrn
MODFILE_PATTERNS test/pynrn/*.mod)
NAME pytest_coreneuron
MODFILE_PATTERNS test/pytest_coreneuron/*.mod)
# In case of multiple Python versions, run these tests with all of them
foreach(val RANGE ${NRN_PYTHON_ITERATION_LIMIT})
list(GET NRN_PYTHON_EXECUTABLES ${val} exe)
list(GET NRN_PYTHON_VERSIONS ${val} pyver)
nrn_add_test(
GROUP pynrn
NAME basic_tests_py${pyver}
PRELOAD_SANITIZER
COMMAND "${exe}" ${pytest} ./test/pynrn
SCRIPT_PATTERNS test/pynrn/*.json test/pynrn/*.py)
foreach(group pytest pytest_coreneuron)
nrn_add_test(
GROUP ${group}
NAME basic_tests_py${pyver}
PRELOAD_SANITIZER
COMMAND "${exe}" ${pytest} "./test/${group}"
SCRIPT_PATTERNS "test/${group}/*.json" "test/${group}/*.py")
endforeach()
endforeach()

# Add some tests that are specifically aimed at NEURON - Python integration, covering different
Expand Down Expand Up @@ -321,7 +330,7 @@ if(NRN_ENABLE_PYTHON)
endif()
endif()
endif()
nrn_add_test_group(NAME parallel MODFILE_PATTERNS test/pynrn/*.mod)
nrn_add_test_group(NAME parallel MODFILE_PATTERNS test/pytest_coreneuron/*.mod)
nrn_add_test(
GROUP parallel
NAME subworld
Expand All @@ -335,17 +344,17 @@ if(NRN_ENABLE_PYTHON)
NAME partrans
PROCESSORS 2
REQUIRES mpi
SCRIPT_PATTERNS test/pynrn/test_partrans.py
SCRIPT_PATTERNS test/pytest_coreneuron/test_partrans.py
COMMAND ${MPIEXEC_NAME} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_PREFLAGS}
nrniv ${MPIEXEC_POSTFLAGS} -mpi -python test/pynrn/test_partrans.py)
nrniv ${MPIEXEC_POSTFLAGS} -mpi -python test/pytest_coreneuron/test_partrans.py)
nrn_add_test(
GROUP parallel
NAME netpar
PROCESSORS 2
REQUIRES mpi
SCRIPT_PATTERNS test/pynrn/test_hoc_po.py test/pynrn/test_netpar.py
SCRIPT_PATTERNS test/pytest_coreneuron/test_hoc_po.py test/pytest_coreneuron/test_netpar.py
COMMAND ${MPIEXEC_NAME} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_PREFLAGS}
nrniv ${MPIEXEC_POSTFLAGS} -mpi -python test/pynrn/test_netpar.py)
nrniv ${MPIEXEC_POSTFLAGS} -mpi -python test/pytest_coreneuron/test_netpar.py)
nrn_add_test(
GROUP parallel
NAME bas
Expand All @@ -363,10 +372,11 @@ if(NRN_ENABLE_PYTHON)
PROCESSORS 2
REQUIRES mpi
ENVIRONMENT "NRN_PYTEST_ARGS=${pytest_arg_string}"
SCRIPT_PATTERNS test/pynrn/run_pytest.py test/pynrn/test_nrntest_fast.json
test/pynrn/test_nrntest_fast.py
SCRIPT_PATTERNS
test/pytest_coreneuron/run_pytest.py test/pytest_coreneuron/test_nrntest_fast.json
test/pytest_coreneuron/test_nrntest_fast.py
COMMAND ${MPIEXEC_NAME} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_PREFLAGS}
special ${MPIEXEC_POSTFLAGS} -mpi -python test/pynrn/run_pytest.py)
special ${MPIEXEC_POSTFLAGS} -mpi -python test/pytest_coreneuron/run_pytest.py)
# CoreNEURON's reports require MPI and segfault if it is not initialised. This is a crude
# workaround.
if(CORENRN_ENABLE_REPORTING)
Expand Down Expand Up @@ -429,11 +439,8 @@ if(NRN_ENABLE_PYTHON)
${MPIEXEC_POSTFLAGS})
endif()

# External coreneuron can be used for testing but for simplicity we are testing only submodule
# builds (in near future we want to support only internal builds anyway). This test uses the
# standard NEURON installation that does not have a `special` statically linked against
# CoreNEURON, so we cannot run this in static builds.
if(NOT nrn_using_ext_corenrn AND CORENRN_ENABLE_SHARED)
# This test uses the standard NEURON installation (without nrnivmodl having been run)
if(CORENRN_ENABLE_SHARED)
nrn_add_test_group(
CORENEURON
NAME coreneuron_standalone
Expand Down Expand Up @@ -488,15 +495,16 @@ if(NRN_ENABLE_PYTHON)
SCRIPT_PATTERNS test/coreneuron/test_spikes.py
MODFILE_PATTERNS
"test/coreneuron/mod files/*.mod" "test/coreneuron/mod files/axial.inc"
test/pynrn/unitstest.mod test/pynrn/version_macros.mod test/gjtests/natrans.mod)
test/pytest_coreneuron/unitstest.mod test/pytest_coreneuron/version_macros.mod
test/gjtests/natrans.mod)
nrn_add_test(
GROUP coreneuron_modtests
NAME version_macros
REQUIRES coreneuron ${modtests_preload_sanitizer}
SCRIPT_PATTERNS test/pynrn/test_version_macros.py
SCRIPT_PATTERNS test/pytest_coreneuron/test_version_macros.py
ENVIRONMENT COVERAGE_FILE=.coverage.coreneuron_version_macros NRN_CORENEURON_ENABLE=true
${sonata_zero_gid_env} ${nrnpython_mpi_env}
COMMAND ${modtests_launch_py} test/pynrn/test_version_macros.py)
COMMAND ${modtests_launch_py} test/pytest_coreneuron/test_version_macros.py)
# In GPU builds run all of the tests on both CPU and GPU
set(coreneuron_modtests_gpu_env CORENRN_ENABLE_GPU=true)
foreach(processor cpu gpu)
Expand Down Expand Up @@ -544,10 +552,10 @@ if(NRN_ENABLE_PYTHON)
GROUP coreneuron_modtests
NAME fast_imem_py_${processor}
REQUIRES coreneuron ${processor} ${modtests_preload_sanitizer}
SCRIPT_PATTERNS test/pynrn/test_fast_imem.py
SCRIPT_PATTERNS test/pytest_coreneuron/test_fast_imem.py
ENVIRONMENT ${modtests_processor_env} ${nrnpython_mpi_env}
COVERAGE_FILE=.coverage.coreneuron_fast_imem_py
COMMAND ${modtests_launch_py} test/pynrn/test_fast_imem.py)
COMMAND ${modtests_launch_py} test/pytest_coreneuron/test_fast_imem.py)
nrn_add_test(
GROUP coreneuron_modtests
NAME datareturn_py_${processor}
Expand Down Expand Up @@ -735,7 +743,7 @@ function(add_modlunit_test mod_file)
"${TESTS}"
PARENT_SCOPE)
endfunction()
add_modlunit_test("${PROJECT_SOURCE_DIR}/test/pynrn/unitstest.mod")
add_modlunit_test("${PROJECT_SOURCE_DIR}/test/pytest_coreneuron/unitstest.mod")
add_modlunit_test("${PROJECT_SOURCE_DIR}/src/nrnoc/hh.mod")
add_modlunit_test("${PROJECT_SOURCE_DIR}/src/nrnoc/stim.mod")
add_modlunit_test("${PROJECT_SOURCE_DIR}/src/nrnoc/pattern.mod")
Expand Down
17 changes: 6 additions & 11 deletions test/coreneuron/test_spikes.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,6 @@ def test_spikes(
nrn_spike_t = nrn_spike_t.to_python()
nrn_spike_gids = nrn_spike_gids.to_python()

# CORENEURON run
from neuron import coreneuron

coreneuron.enable = True
coreneuron.gpu = enable_gpu
coreneuron.file_mode = file_mode
coreneuron.verbose = 0
corenrn_all_spike_t = h.Vector()
corenrn_all_spike_gids = h.Vector()

Expand Down Expand Up @@ -122,11 +115,13 @@ def run(mode):
assert nrn_spike_t == corenrn_all_spike_t_py
assert nrn_spike_gids == corenrn_all_spike_gids_py

if file_mode is False:
for mode in [0, 1, 2]:
# CORENEURON run
from neuron import coreneuron

with coreneuron(enable=True, gpu=enable_gpu, file_mode=file_mode, verbose=0):
run_modes = [0] if file_mode else [0, 1, 2]
for mode in run_modes:
run(mode)
else:
run(0)

return h

Expand Down
2 changes: 1 addition & 1 deletion test/external/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ FetchContent_Declare(
FetchContent_Declare(
nrntest
GIT_REPOSITORY https://github.com/neuronsimulator/nrntest
GIT_TAG 0cc9c95425b6a9e4d222be1b4cc912d50a274a2f
GIT_TAG fe7029e38f518787fd61fb61b0d861f680cbcf39
SOURCE_DIR ${PROJECT_SOURCE_DIR}/external/tests/nrntest)

FetchContent_Declare(
Expand Down
File renamed without changes.
Loading

0 comments on commit e029076

Please sign in to comment.