From 1cad60d61625e2c6ecc180e8be26b309913ef220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandru=20S=C4=83vulescu?= Date: Thu, 8 Feb 2024 22:20:53 +0100 Subject: [PATCH 01/26] Auto-apply formatting from PR (#2395) when the code formatting check fails, run code formatting and push the changes to the same PR branch. --- .github/workflows/formatting.yml | 21 +++++++++++++++++++++ src/nrniv/netpar.cpp | 2 ++ 2 files changed, 23 insertions(+) diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index ab3e59723d..8b6dbf62db 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -27,3 +27,24 @@ jobs: - name: Check formatting working-directory: ${{runner.workspace}}/nrn run: external/coding-conventions/bin/format -v --dry-run + + # If formatting fails, apply formatting and push changes. + # This will trigger another workflow run, which will cancel the current one. + - name: Apply formatting + working-directory: ${{runner.workspace}}/nrn + if: failure() && github.event_name == 'pull_request' + run: | + # Checkout PR + gh pr checkout ${{ github.event.pull_request.number }} + + # Apply formatting + external/coding-conventions/bin/format -v + + # Commit & push changes + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -u :/ + git commit -a -m "Fix formatting" + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/nrniv/netpar.cpp b/src/nrniv/netpar.cpp index d3fd888479..249d577a2b 100644 --- a/src/nrniv/netpar.cpp +++ b/src/nrniv/netpar.cpp @@ -240,6 +240,8 @@ NetParEvent::~NetParEvent() {} void NetParEvent::send(double tt, NetCvode* nc, NrnThread* nt) { nc->event(tt + usable_mindelay_, this, nt); } + + void NetParEvent::deliver(double tt, NetCvode* nc, NrnThread* nt) { int seq; if (nrn_use_selfqueue_) { // first handle pending flag=1 self events From 17bd2c8ecd0f7483675f2c27236d1d52b3901798 Mon Sep 17 00:00:00 2001 From: Pramod Kumbhar Date: Fri, 9 Feb 2024 08:25:16 +0100 Subject: [PATCH 02/26] Provide NMODL binary path for RANDOM test (#2717) - if NMODL is installed externally, it's not in $PATH and hence test_nmodlrandom_syntax.py was failing (see BlueBrain/nmodl/pull/1150) - provide the path NMODL binary from CMake to avoid this issue --- CMakeLists.txt | 1 + src/coreneuron/CMakeLists.txt | 2 +- test/CMakeLists.txt | 6 ++++-- test/coreneuron/test_nmodlrandom_syntax.py | 5 ++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c35b8507fd..85dc3cd3d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -521,6 +521,7 @@ if(NRN_ENABLE_CORENEURON) get_property(CORENRN_LIB_LINK_FLAGS GLOBAL PROPERTY CORENRN_LIB_LINK_FLAGS) get_property(CORENRN_NEURON_LINK_FLAGS GLOBAL PROPERTY CORENRN_NEURON_LINK_FLAGS) get_property(CORENRN_ENABLE_SHARED GLOBAL PROPERTY CORENRN_ENABLE_SHARED) + get_property(CORENRN_NMODL_BINARY GLOBAL PROPERTY CORENRN_NMODL_BINARY) # NEURON tests that link against CoreNEURON need to depend on it. set(CORENEURON_TARGET_TO_DEPEND coreneuron-for-tests) diff --git a/src/coreneuron/CMakeLists.txt b/src/coreneuron/CMakeLists.txt index 2552c80abd..561b9896e3 100644 --- a/src/coreneuron/CMakeLists.txt +++ b/src/coreneuron/CMakeLists.txt @@ -283,8 +283,8 @@ else() FILES ${NMODL_PROJECT_BINARY_DIR}/share/nmodl/nrnunits.lib DESTINATION share/nmodl COMPONENT nrnunits) - endif() +set_property(GLOBAL PROPERTY CORENRN_NMODL_BINARY "${CORENRN_NMODL_BINARY}") # set correct arguments for nmodl for cpu/gpu target set(CORENRN_NMODL_FLAGS diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c1e5411f03..86d011562b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -655,8 +655,10 @@ if(NRN_ENABLE_PYTHON) NAME test_nmodlrandom_syntax_py_${processor} REQUIRES coreneuron ${processor} ${modtests_preload_sanitizer} SCRIPT_PATTERNS test/coreneuron/test_nmodlrandom_syntax.py - ENVIRONMENT ${modtests_processor_env} ${nrnpython_mpi_env} - COVERAGE_FILE=.coverage.coreneuron_test_nmodlrandom_syntax_py + ENVIRONMENT + ${modtests_processor_env} ${nrnpython_mpi_env} + COVERAGE_FILE=.coverage.coreneuron_test_nmodlrandom_syntax_py + NMODL_BINARY=${CORENRN_NMODL_BINARY} COMMAND ${modtests_launch_py} test/coreneuron/test_nmodlrandom_syntax.py) nrn_add_test( GROUP coreneuron_modtests diff --git a/test/coreneuron/test_nmodlrandom_syntax.py b/test/coreneuron/test_nmodlrandom_syntax.py index 1dbe14ec61..9e04092a7b 100644 --- a/test/coreneuron/test_nmodlrandom_syntax.py +++ b/test/coreneuron/test_nmodlrandom_syntax.py @@ -1,6 +1,7 @@ from neuron import h import subprocess from pathlib import Path +import os # default args generate accepted nmodl string @@ -75,7 +76,9 @@ def chk_nmodl(txt, program="nocmodl", rcode=False): def test_syntax(): - for program in ["nocmodl", "nmodl"]: + # nmodl could be external installation (not in PATH) + nmodl_binary = os.environ.get("NMODL_BINARY", "nmodl") + for program in ["nocmodl", nmodl_binary]: foo = False assert chk_nmodl(modfile(), program, rcode=True) assert chk_nmodl(modfile(s0="ASSIGNED{rv1}"), program) From 28134500b46fbe7da8002ab6dc370c19c70a8432 Mon Sep 17 00:00:00 2001 From: JCGoran Date: Fri, 9 Feb 2024 18:21:27 +0100 Subject: [PATCH 03/26] Add setuptools to install_requires (#2720) When installing a pre-built wheel in a venv on Python 3.12, calling `nrniv` fails because the wrapper script uses `setuptools` for some functionality, so it should also be added as a runtime dependency. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5f3e3035ff..bccf1bed53 100644 --- a/setup.py +++ b/setup.py @@ -509,7 +509,7 @@ def setup_package(): else "node-and-date" }, cmdclass=dict(build_ext=CMakeAugmentedBuilder, docs=Docs), - install_requires=["numpy>=1.9.3", "packaging", "find_libpython"], + install_requires=["numpy>=1.9.3", "packaging", "find_libpython", "setuptools"], tests_require=["flake8", "pytest"], setup_requires=["wheel", "setuptools_scm"] + maybe_docs From 4156d23ff330584332ccf6c199c7aec9fcb682e9 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Wed, 14 Feb 2024 12:51:34 +0100 Subject: [PATCH 04/26] CircleCI: stop upgrading pyenv (#2723) It is no more possible to git fetch the pyenv because this is owned by root. So just remove this step because we don't really need it anymore (3.12 is now available). --- .circleci/config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ec6f56ba6..737c5cc777 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,10 +54,9 @@ jobs: 310) pyenv_py_ver="3.10" ;; 311) pyenv_py_ver="3.11" ;; 312) pyenv_py_ver="3.12" ;; - *) echo "Error: pyenv python version not specified!" && exit 1;; + *) echo "Error: pyenv python version not specified or not supported." && exit 1;; esac - cd /opt/circleci/.pyenv/plugins/python-build/../.. && git fetch --all && git checkout -B master origin/master && cd - env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install $pyenv_py_ver --force pyenv global $pyenv_py_ver export PYTHON_EXE=$(which python) From 6c89db8560af352a04ccabf0df267e32eef73531 Mon Sep 17 00:00:00 2001 From: rgourdine <88497408+rgourdine@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:50:26 -0500 Subject: [PATCH 05/26] Add node in segment functionality (#2737) --- share/lib/python/neuron/rxd/node.py | 3 +++ src/nrnpython/nrnpy_nrn.cpp | 24 +++++++++++++++++------- src/nrnpython/nrnpy_nrn.h | 1 + test/rxd/test_contains.py | 29 +++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/share/lib/python/neuron/rxd/node.py b/share/lib/python/neuron/rxd/node.py index 0a67c60caa..4bb7782513 100644 --- a/share/lib/python/neuron/rxd/node.py +++ b/share/lib/python/neuron/rxd/node.py @@ -468,6 +468,9 @@ def volume(self): def segment(self): return self._sec._sec(self.x) + def _in_seg(self, segment): + return segment == self.segment + @property def surface_area(self): """The surface area of the compartment in square microns. diff --git a/src/nrnpython/nrnpy_nrn.cpp b/src/nrnpython/nrnpy_nrn.cpp index c875edc10e..4635c35e25 100644 --- a/src/nrnpython/nrnpy_nrn.cpp +++ b/src/nrnpython/nrnpy_nrn.cpp @@ -181,21 +181,26 @@ static Object* pysec_cell(Section* sec) { return NULL; } -static int NPySecObj_contains(PyObject* sec, PyObject* obj) { - /* report that we contain the object if it has a .sec that is equal to ourselves */ - PyObject* obj_sec; +static int NpySObj_contains(PyObject* s, PyObject* obj, const char* string) { + /* Checks is provided PyObject* s contains obj */ + PyObject* obj_seg; int result; - if (!PyObject_HasAttrString(obj, "sec")) { + if (!PyObject_HasAttrString(obj, string)) { return 0; } Py_INCREF(obj); - obj_sec = PyObject_GetAttrString(obj, "sec"); + obj_seg = PyObject_GetAttrString(obj, string); Py_DECREF(obj); - result = PyObject_RichCompareBool(sec, obj_sec, Py_EQ); - Py_XDECREF(obj_sec); + result = PyObject_RichCompareBool(s, obj_seg, Py_EQ); + Py_XDECREF(obj_seg); return (result); } +static int NPySecObj_contains(PyObject* sec, PyObject* obj) { + /* report that we contain the object if it has a .sec that is equal to ourselves */ + return NpySObj_contains(sec, obj, "sec"); +} + static int pysec_cell_equals(Section* sec, Object* obj) { if (auto* pv = sec->prop->dparam[PROP_PY_INDEX].get(); sec->prop && pv) { PyObject* cell_weakref = static_cast(pv)->cell_weakref_; @@ -459,6 +464,11 @@ static PyObject* NPyMechObj_new(PyTypeObject* type, PyObject* args, PyObject* kw return (PyObject*) self; } +static int NPySegObj_contains(PyObject* segment, PyObject* obj) { + /* report that we contain the object if it has a .segment that is equal to ourselves */ + return NpySObj_contains(segment, obj, "segment"); +} + static PyObject* NPyRangeVar_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { NPyRangeVar* self; self = (NPyRangeVar*) type->tp_alloc(type, 0); diff --git a/src/nrnpython/nrnpy_nrn.h b/src/nrnpython/nrnpy_nrn.h index 1153eed2cc..71ebca1d1e 100644 --- a/src/nrnpython/nrnpy_nrn.h +++ b/src/nrnpython/nrnpy_nrn.h @@ -69,6 +69,7 @@ static PyType_Slot nrnpy_SegmentType_slots[] = { {Py_tp_init, (void*) NPySegObj_init}, {Py_tp_new, (void*) NPySegObj_new}, {Py_tp_doc, (void*) "Segment objects"}, + {Py_sq_contains, (void*) NPySegObj_contains}, {0, 0}, }; static PyType_Spec nrnpy_SegmentType_spec = { diff --git a/test/rxd/test_contains.py b/test/rxd/test_contains.py index 304b0861e1..b1f46d40ba 100644 --- a/test/rxd/test_contains.py +++ b/test/rxd/test_contains.py @@ -23,3 +23,32 @@ def test_in(neuron_instance): assert node not in soma assert node in cyt assert node not in er + + +def test_in_segment(neuron_instance): + """Test 1D diffusion in a single segment""" + h, rxd, data, save_path = neuron_instance + + dend1 = h.Section("dend1") + dend2 = h.Section("dend2") + dend1.nseg = 4 + + cyt1 = rxd.Region(dend1.wholetree(), nrn_region="i") + ca1 = rxd.Species(cyt1, name="ca1", charge=2, initial=1e-12) + + node1 = ca1.nodes(dend1(0.5))[0] + node2 = ca1.nodes(dend1(0.3))[0] + + assert node1 in dend1 + assert node1 not in dend2 + assert node1 not in dend1(0.125) + assert node1 not in dend1(0.375) + assert node1 in dend1(0.625) + assert node1 not in dend1(0.875) + + assert node2 in dend1 + assert node2 not in dend2 + assert node2 not in dend1(0.125) + assert node2 in dend1(0.375) + assert node2 not in dend1(0.625) + assert node2 not in dend1(0.875) From 23c00e292234b12b326fb56e99cb783877a5b865 Mon Sep 17 00:00:00 2001 From: Luc Grosheintz Date: Thu, 15 Feb 2024 08:37:27 +0100 Subject: [PATCH 06/26] Show full path compiled MOD files (#2729) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to this commit the output on failure would be: [ 78%] Built target hoc_module hhwatch.cpp: In function ‘void _hoc_destroy_pnt(void*)’: hhwatch.cpp:175:61: error: cannot convert ‘DatumHandle’ {aka ‘neuron::container::Mechanism::DatumHandle’} to ‘Datum*’ {aka ‘neuron::container::generic_data_handle*’} which isn't helpful because it's not obvious which `hhwatch.cpp` contains the faulty code. --- bin/nrnivmodl.in | 4 ++-- bin/nrnivmodl_makefile_cmake.in | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/nrnivmodl.in b/bin/nrnivmodl.in index 100c173cc9..17c0c51a83 100755 --- a/bin/nrnivmodl.in +++ b/bin/nrnivmodl.in @@ -170,11 +170,11 @@ for i in "${files[@]}" ; do f=${f// /\\ } f=${f//:/\\:} echo "\ -./${base_name// /\\ }.cpp: ${f}.mod +\$(PWD)/${base_name// /\\ }.cpp: ${f}.mod @printf \" -> \$(C_GREEN)NMODL\$(C_RESET) \$<\\\n\" (cd \"$dir_name\"; @NRN_NOCMODL_SANITIZER_ENVIRONMENT_STRING@ MODLUNIT=\$(NRNUNITS) \$(NOCMODL) \"$base_name.mod\" -o \"$mdir\" $UserNMODLFLAGS) -./${base_name// /\\ }.o: ./${base_name// /\\ }.cpp +./${base_name// /\\ }.o: \$(PWD)/${base_name// /\\ }.cpp @printf \" -> \$(C_GREEN)Compiling\$(C_RESET) \$<\\\n\" \$(CXXCOMPILE) -I\"$dir_name\" \$(INCLUDES) @CMAKE_CXX_COMPILE_OPTIONS_PIC@ -c \$< -o \$@ " >> "$MODMAKE" diff --git a/bin/nrnivmodl_makefile_cmake.in b/bin/nrnivmodl_makefile_cmake.in index 0bbf3d9375..4894ac4c72 100644 --- a/bin/nrnivmodl_makefile_cmake.in +++ b/bin/nrnivmodl_makefile_cmake.in @@ -11,9 +11,9 @@ # Mechanisms version are by default 0.0, but should be overriden MECH_NAME = MECH_VERSION = 0.0 -MODS_PATH = . +MODS_PATH = $(PWD) # in the @CMAKE_HOST_SYSTEM_PROCESSOR@ folder -OUTPUT = . +OUTPUT = $(PWD) DESTDIR = UserINCFLAGS = UserLDFLAGS = @@ -78,8 +78,8 @@ endif NRNUNITS = $(datadir_lib)/nrnunits.lib # File path config (internal) -MODC_DIR = . -OBJS_DIR = . +MODC_DIR = $(PWD) +OBJS_DIR = $(PWD) mod_objs = $(MODOBJFILES) mod_func_o = $(OBJS_DIR)/mod_func.o From c7d200c99e959e2675a38fab7e414b1cc3b227ee Mon Sep 17 00:00:00 2001 From: Luc Grosheintz Date: Thu, 15 Feb 2024 09:37:23 +0100 Subject: [PATCH 07/26] Regenerated code if code-generator changes (#2734) While development, the code-generator might be changed. This could result in a different `.cpp` file. However, in the make file snippet generated by `nrnivmodl` the generated `.cpp` file doesn't have a dependency on `nocmodl`. This means if it changes the `.cpp` doesn't need to be recreated. Since `nrnivmodl` is called from `CMakeLists.txt` this can cause broken builds, where changes to the source files aren't reflected in the generated binaries. --- bin/nrnivmodl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nrnivmodl.in b/bin/nrnivmodl.in index 17c0c51a83..7cc200a6e4 100755 --- a/bin/nrnivmodl.in +++ b/bin/nrnivmodl.in @@ -170,7 +170,7 @@ for i in "${files[@]}" ; do f=${f// /\\ } f=${f//:/\\:} echo "\ -\$(PWD)/${base_name// /\\ }.cpp: ${f}.mod +\$(PWD)/${base_name// /\\ }.cpp: ${f}.mod \$(NOCMODL) @printf \" -> \$(C_GREEN)NMODL\$(C_RESET) \$<\\\n\" (cd \"$dir_name\"; @NRN_NOCMODL_SANITIZER_ENVIRONMENT_STRING@ MODLUNIT=\$(NRNUNITS) \$(NOCMODL) \"$base_name.mod\" -o \"$mdir\" $UserNMODLFLAGS) From 776f850f0c90698b16f954b86107a69f4fe73ed7 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Thu, 15 Feb 2024 17:04:21 +0100 Subject: [PATCH 08/26] Keep only the queue that is used (#2740) --- src/nrncvode/bbtqueue.cpp | 488 ------------------------------------- src/nrncvode/bbtqueue.h | 62 ----- src/nrncvode/cvodeobj.cpp | 10 - src/nrncvode/netcvode.cpp | 74 ------ src/nrncvode/netcvode.h | 5 - src/nrncvode/spt2queue.cpp | 272 --------------------- src/nrncvode/spt2queue.h | 64 ----- src/nrncvode/sptqueue.cpp | 194 --------------- src/nrncvode/sptqueue.h | 57 ----- src/nrncvode/sptree.h | 9 - src/nrncvode/tqueue.cpp | 8 - src/nrncvode/tqueue.h | 11 - src/nrniv/netpar.cpp | 4 - 13 files changed, 1258 deletions(-) delete mode 100644 src/nrncvode/bbtqueue.cpp delete mode 100644 src/nrncvode/bbtqueue.h delete mode 100644 src/nrncvode/spt2queue.cpp delete mode 100644 src/nrncvode/spt2queue.h delete mode 100644 src/nrncvode/sptqueue.cpp delete mode 100644 src/nrncvode/sptqueue.h diff --git a/src/nrncvode/bbtqueue.cpp b/src/nrncvode/bbtqueue.cpp deleted file mode 100644 index ce31f5c72a..0000000000 --- a/src/nrncvode/bbtqueue.cpp +++ /dev/null @@ -1,488 +0,0 @@ -// balanced binary tree queue implemented by Michael Hines - -TQItem::TQItem() { - left_ = nullptr; - right_ = nullptr; - parent_ = nullptr; -} - -TQItem::~TQItem() { - if (left_) { - delete left_; - } - if (right_) { - delete right_; - } -} - -static void deleteitem(TQItem* i) { - if (i->left_) { - deleteitem(i->left_); - } - if (i->right_) { - deleteitem(i->right_); - } - i->left_ = nullptr; - i->right_ = nullptr; - tpool_->free(i); -} - -bool TQItem::check() { -#if DOCHECK - if (left_ && left_->t_ > t_) { - printf("left %g not <= %g\n", left_->t_, t_); - return false; - } - if (right_ && right_->t_ < t_) { - printf("right %g not >= %g\n", right_->t_, t_); - return false; - } - if (parent_) { - if (parent_->left_ == this) { - if (t_ > parent_->t_) { - printf("%g not <= parent %g\n", t_, parent_->t_); - return false; - } - } else if (parent_->right_ == this) { - if (t_ < parent_->t_) { - printf("%g not >= parent %g\n", t_, parent_->t_); - return false; - } - } else { - printf("this %g is not a child of its parent %g\n", t_, parent_->t_); - return false; - } - } - if (w_ != wleft() + wright() + 1) { - printf("%g: weight %d inconsistent with left=%d and right=%d\n", t_, w_, wleft(), wright()); - return false; - } -#endif - return true; -} - -static void prnt(const TQItem* b, int level) { - int i; - for (i = 0; i < level; ++i) { - Printf(" "); - } - Printf("%g %c %d\n", b->t_, b->data_ ? 'x' : 'o', b->w_); -} - -static void chk(TQItem* b, int level) { - if (!b->check()) { - hoc_execerror("chk failed", errmess_); - } -} - -void TQItem::t_iterate(void (*f)(const TQItem*, int), int level) { - if (left_) { - left_->t_iterate(f, level + 1); - } - f(this, level); - if (right_) { - right_->t_iterate(f, level + 1); - } -} - -#if BBTQ == 0 // balanced binary tree implemented by Michael Hines - -TQueue::TQueue() { - if (!tpool_) { - tpool_ = new TQItemPool(1000); - } - root_ = nullptr; - least_ = nullptr; - -#if COLLECT_TQueue_STATISTICS - nmove = ninsert = nrem = nleast = nbal = ncmplxrem = 0; - nfastmove = ncompare = nleastsrch = nfind = nfindsrch = 0; -#endif -} - -TQueue::~TQueue() { - if (root_) { - deleteitem(root_); - } -} - -void TQueue::print() { - if (root_) { - root_->t_iterate(prnt, 0); - } -} - -void TQueue::forall_callback(void (*f)(const TQItem*, int)) { - if (root_) { - root_->t_iterate(f, 0); - } -} - -void TQueue::check(const char* mes) { -#if DOCHECK - errmess_ = mes; - if (root_) { - root_->t_iterate(chk, 0); - } - errmess_ = nullptr; -#endif -} - -double TQueue::least_t() { - TQItem* b = least(); - if (b) { - return b->t_; - } else { - return 1e15; - } -} - -TQItem* TQueue::least() { - STAT(nleast) -#if DOCHECK - TQItem* b; - b = root_; - if (b) - for (; b->left_; b = b->left_) { - STAT(nleastsrch) - } -#if DOCHECK - assert(least_ == b); -#else - least_ = b; -#endif -#endif - return least_; -} - -void TQueue::new_least() { - assert(least_); - assert(!least_->left_); - TQItem* b = least_->right_; - if (b) { - for (; b->left_; b = b->left_) { - ; - } - least_ = b; - } else { - b = least_->parent_; - if (b) { - assert(b->left_ == least_); - least_ = b; - } else { - least_ = nullptr; - } - } -} - -void TQueue::move_least(double tnew) { - TQItem* b = least(); - if (b) { - move(b, tnew); - } -} - -void TQueue::move(TQItem* i, double tnew) { - PSTART(1) - STAT(nmove) -#if 0 - // this is a bug - // check if it really needs moving - STAT(ncompare) - double tmin = -1e9, tmax = 1e9; - if (i->left_) { - tmin = i->left_->t_; - }else if (i->parent_ && i->parent_->right_ == i) { - tmin = i->parent_->t_; - } - if (i->right_) { - tmax = i->right_->t_; - }else if (i->parent_ && i->parent_->left_ == i) { - tmax = i->parent_->t_; - } - if ( tmin <= tnew && tnew < tmax) { // confusing to check equality - STAT(nfastmove) - i->t_ = tnew; -i->check(); -PSTOP(1) - return; - } -#endif - // the long way - remove1(i); - insert1(tnew, i); - PSTOP(1) -} - -TQItem* TQueue::find(double t) { - TQItem* b; - STAT(nfind) - for (b = root_; b;) { - STAT(nfindsrch) - STAT(ncompare) - if (t == b->t_) { - // printf("found %g\n", t); - return b; - } else if (t < b->t_) { - b = b->left_; - } else { - b = b->right_; - } - } - Printf("couldn't find %g\n", t); - return b; -} - -void TQueue::remove(TQItem* i) { - PSTART(1) - if (i) { - remove1(i); - deleteitem(i); - } - PSTOP(1) -} - -void TQueue::remove1(TQItem* i) { - STAT(nrem) - // merely remove if leaf - // if no left, right becomes left of parent - // replace i with rightmost on left (say x) - // we could just replace i.data with x.data - // and remove x. But then, data holding pointer to - // x would be invalid. So take the trouble to reset - // pointer belonging to x - check("begin remove1"); - if (least_ && least_ == i) { - new_least(); - } - TQItem* p = i->parent_; - TQItem** child; - bool doweight = true; - if (p) { - // printf("removing with a parent %g\n", i->t_); - if (p->left_ == i) { - child = &p->left_; - } else { - child = &p->right_; - } - } - - if (i->left_) { - STAT(ncmplxrem); - // printf("removing with a left %g\n", i->t_); - // replace i with rightmost on left - TQItem* x; - for (x = i->left_; x->right_; x = x->right_) { - ; - } - if (x == i->left_) { - // printf("x == i->left\n"); - if (p) { - *child = x; - } else { - root_ = x; - } - x->parent_ = i->parent_; - x->right_ = i->right_; - if (x->right_) { - x->right_->parent_ = x; - // x->w_ += x->right_->w_; - x->w_ = i->w_ - 1; - } - } else { - remove1(x); - doweight = false; - if (p) { - *child = x; - } else { - root_ = x; - } - x->parent_ = i->parent_; - x->right_ = i->right_; - x->left_ = i->left_; - x->left_->parent_ = x; - if (x->right_) { - x->right_->parent_ = x; - } - x->w_ = i->w_; - } - } else if (i->right_) { - // printf("removing with a right but no left %g\n", i->t_); - // check(); printf("checked\n"); - // no left - if (p) { - *child = i->right_; - (*child)->parent_ = p; - } else { - root_ = i->right_; - root_->parent_ = nullptr; - } - } else { - // a leaf - // printf("removing leaf %g\n", i->t_); - if (p) { - *child = nullptr; - } else { - root_ = nullptr; - } - } - if (doweight) { - while (p) { - --p->w_; - p = p->parent_; - } - } - i->right_ = nullptr; - i->left_ = nullptr; - i->parent_ = nullptr; - check("end remove1"); -} - -void TQueue::reverse(TQItem* b) { // switch item and parent - TQItem* p = b->parent_; - if (p) { - STAT(nbal) - if (p->parent_) { - if (!p->parent_->check()) - Printf("p->parent failed start\n"); - } - if (!p->check()) - Printf("p failed at start\n"); - if (!b->check()) - Printf("b failed at start\n"); - if (p->parent_) { - b->parent_ = p->parent_; - if (p->parent_->left_ == p) { - p->parent_->left_ = b; - } else { - p->parent_->right_ = b; - } - } else { - assert(root_ == p); - b->parent_ = nullptr; - root_ = b; - } - b->w_ = p->w_; - p->parent_ = b; - if (p->left_ == b) { - p->left_ = b->right_; - b->right_ = p; - if (p->left_) { - p->left_->parent_ = p; - } - } else { - p->right_ = b->left_; - b->left_ = p; - if (p->right_) { - p->right_->parent_ = p; - } - } - p->w_ = p->wleft() + p->wright() + 1; - if (b->parent_) { - if (!b->parent_->check()) - Printf("b->parent failed end\n"); - } - if (!p->check()) - Printf("p failed at end\n"); - if (!b->check()) - Printf("b failed at end\n"); - } -} - -TQItem* TQueue::insert(double t, void* data) { - PSTART(1) - // TQItem* i = new TQItem(); - TQItem* i = tpool_->alloc(); - i->data_ = data; - insert1(t, i); - PSTOP(1) - return i; -} - -void TQueue::insert1(double t, TQItem* i) { - check("begin insert1"); - STAT(ncompare) - if (!least_ || t < least_->t_) { - least_ = i; - } - TQItem* p = root_; - STAT(ninsert) - if (p) { - for (;;) { - STAT(ncompare) - if (t < p->t_) { - if (p->left_) { - p = p->left_; - // if (p->unbalanced()) { - // reverse(p); - // } - } else { - p->left_ = i; - break; - } - } else { - if (p->right_) { - p = p->right_; - // if (p->unbalanced()) { - // reverse(p); - // } - } else { - p->right_ = i; - break; - } - } - } - } else { - root_ = i; - } - i->parent_ = p; - i->t_ = t; - i->w_ = 1; - for (p = i->parent_; p; p = p->parent_) { - ++p->w_; - } - for (p = i->parent_; p;) { - if (p->unbalanced()) { - reverse(p); - } else { - p = p->parent_; - } - } - check("end insert1"); -} - -bool TQItem::unbalanced() { - int balance, dbalance; - if (parent_) { - balance = parent_->wright() - parent_->wleft(); - if (parent_->left_ == (TQItem*) this) { - if (balance < -(wright() + 1)) { - // printf("balance %d to %d\n", balance, balance + 2*(wright()+1)); - return true; - } - } else { - if (balance > wleft() + 1) { - // printf("balance %d to %d\n", balance, balance - 2*(wleft()+1)); - return true; - } - } - } - return false; -} - -void TQueue::statistics() { -#if COLLECT_TQueue_STATISTICS - Printf("insertions=%lu moves=%lu fastmoves=%lu removals=%lu calls to least=%lu\n", - ninsert, - nmove, - nfastmove, - nrem, - nleast); - Printf("calls to find=%lu balances=%lu complex removals=%lu\n", nfind, nbal, ncmplxrem); - Printf("comparisons=%lu leastsearch=%lu findsearch=%lu\n", ncompare, nleastsrch, nfindsrch); -#else - Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.h\n"); -#endif -} - -#endif diff --git a/src/nrncvode/bbtqueue.h b/src/nrncvode/bbtqueue.h deleted file mode 100644 index 15bd8503ff..0000000000 --- a/src/nrncvode/bbtqueue.h +++ /dev/null @@ -1,62 +0,0 @@ -//#ifndef tqueue_h -//#define tqueue_h - -#define COLLECT_TQueue_STATISTICS 1 - -class TQItem { - public: - TQItem(); - virtual ~TQItem(); - bool check(); - int wleft() { - return (left_ ? left_->w_ : 0); - } - int wright() { - return (right_ ? right_->w_ : 0); - } - void t_iterate(void (*)(const TQItem*, int), int); - bool unbalanced(); - void clear() {} - - public: - void* data_; - double t_; - TQItem* left_; - TQItem* right_; - TQItem* parent_; - int w_; -}; - -class TQueue { - public: - TQueue(); - virtual ~TQueue(); - - TQItem* least(); // does not remove from TQueue - double least_t(); - TQItem* insert(double t, void* data_); - TQItem* find(double t); - void remove(TQItem*); - void move(TQItem*, double tnew); - void move_least(double tnew); - void print(); - void check(const char* errmess); - void statistics(); - void insert1(double t, TQItem*); - void remove1(TQItem*); - void forall_callback(void (*)(const TQItem*, int)); - - private: - void reverse(TQItem*); - void new_least(); - - private: - TQItem* least_; - TQItem* root_; -#if COLLECT_TQueue_STATISTICS - private: - unsigned long ninsert, nrem, nleast, nbal, ncmplxrem; - unsigned long ncompare, nleastsrch, nfind, nfindsrch, nmove, nfastmove; -#endif -}; -//#endif diff --git a/src/nrncvode/cvodeobj.cpp b/src/nrncvode/cvodeobj.cpp index 0579d71964..5576c603e1 100644 --- a/src/nrncvode/cvodeobj.cpp +++ b/src/nrncvode/cvodeobj.cpp @@ -79,9 +79,7 @@ extern N_Vector N_VNew_NrnParallelLD(int comm, long int local_length, long int g #endif extern bool nrn_use_fifo_queue_; -#if BBTQ == 5 extern bool nrn_use_bin_queue_; -#endif #undef SUCCESS #define SUCCESS CV_SUCCESS @@ -120,13 +118,6 @@ static double spikestat(void* v) { } static double queue_mode(void* v) { hoc_return_type_code = 1; // integer -#if BBTQ == 4 - if (ifarg(1)) { - nrn_use_fifo_queue_ = chkarg(1, 0, 1) ? true : false; - } - return double(nrn_use_fifo_queue_); -#endif -#if BBTQ == 5 if (ifarg(1)) { nrn_use_bin_queue_ = chkarg(1, 0, 1) ? true : false; } @@ -141,7 +132,6 @@ static double queue_mode(void* v) { #endif } return double(nrn_use_bin_queue_ + 2 * nrn_use_selfqueue_); -#endif return 0.; } diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index f86afcfdce..4c017f54ad 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -246,9 +246,7 @@ extern void nrn_multisend_advance(); bool nrn_use_fifo_queue_; -#if BBTQ == 5 bool nrn_use_bin_queue_; -#endif #if NRNMPI // for compressed info during spike exchange @@ -2549,31 +2547,8 @@ void NetCvode::vec_event_store() { } } -#if BBTQ == 4 -TQItem* NetCvode::fifo_event(double td, DiscreteEvent* db) { - if (nrn_use_fifo_queue_) { -#if PRINT_EVENT - if (print_event_) { - db->pr("send", td, this); - } - if (vec_event_store_) { - Vect* x = vec_event_store_; - int n = x->size(); - x->resize_chunk(n + 2); - x->elem(n) = t; - x->elem(n + 1) = td; - } -#endif - return p[0].tqe_->insert_fifo(td, db); - } else { - return p[0].tqe_->insert(td, db); - } -} -#else #define fifo_event event -#endif -#if BBTQ == 5 TQItem* NetCvode::bin_event(double td, DiscreteEvent* db, NrnThread* nt) { if (nrn_use_bin_queue_) { #if PRINT_EVENT @@ -2597,9 +2572,6 @@ TQItem* NetCvode::bin_event(double td, DiscreteEvent* db, NrnThread* nt) { return p[nt->id].tqe_->insert(td, db); } } -#else -#define bin_event event -#endif TQItem* NetCvode::event(double td, DiscreteEvent* db, NrnThread* nt) { #if PRINT_EVENT @@ -2827,10 +2799,8 @@ void NetCvode::clear_events() { d.selfqueue_->remove_all(); } } -#if BBTQ == 5 d.tqe_->nshift_ = -1; d.tqe_->shift_bin(nt_t - 0.5 * nt_dt); -#endif } // I don't believe this is needed anymore since cvode not needed // til delivery. @@ -2858,7 +2828,6 @@ void NetCvode::free_event_pools() { void NetCvode::init_events() { hoc_Item* q; int i, j; -#if BBTQ == 5 for (i = 0; i < nrn_nthread; ++i) { p[i].tqe_->nshift_ = -1; // first bin starts 1/2 time step early because per time step @@ -2867,7 +2836,6 @@ void NetCvode::init_events() { // nt->_t + 0.5*nt->_dt where nt->_t is a multiple of dt. p[i].tqe_->shift_bin(nt_t - 0.5 * nt_dt); } -#endif if (psl_) { ITERATE(q, psl_) { PreSyn* ps = (PreSyn*) VOIDITM(q); @@ -2879,19 +2847,6 @@ void NetCvode::init_events() { // also decide what to do about use_min_delay_ // the rule for now is to use it if all delays are // the same and there are more than 2 -#if BBTQ == 4 - // but - // if we desire nrn_use_fifo_queue_ then use it - // even if just one - double fifodelay; - if (nrn_use_fifo_queue_) { - if (!dil.empty()) { - ps->use_min_delay_ = 1; - ps->delay_ = dil[0]->delay_; - fifodelay = ps->delay_; - } - } else -#endif // BBTQ { if (dil.size() > 2) { ps->use_min_delay_ = 1; @@ -2904,15 +2859,6 @@ void NetCvode::init_events() { if (ps->use_min_delay_ && ps->delay_ != d->delay_) { ps->use_min_delay_ = false; } -#if BBTQ == 4 - if (nrn_use_fifo_queue_ && d->delay_ != fifodelay) { - hoc_warning( - "Use of the event fifo queue is turned off due to more than one value for " - "NetCon.delay", - 0); - nrn_use_fifo_queue_ = false; - } -#endif } } } @@ -3076,11 +3022,7 @@ void NetCon::send(double tt, NetCvode* ns, NrnThread* nt) { if (active_ && target_) { assert(PP2NT(target_) == nt); STATISTICS(netcon_send_active_); -#if BBTQ == 5 ns->bin_event(tt, this, PP2NT(target_)); -#else - ns->event(tt, this, PP2NT(target_)); -#endif } else { STATISTICS(netcon_send_inactive_); } @@ -3157,10 +3099,6 @@ void PreSyn::send(double tt, NetCvode* ns, NrnThread* nt) { #ifndef USENCS if (use_min_delay_) { STATISTICS(presyn_send_mindelay_); -#if BBTQ == 4 - ns->fifo_event(tt + delay_, this); -#else -#if BBTQ == 5 for (i = 0; i < nrn_nthread; ++i) { if (nt->id == i) { ns->bin_event(tt + delay_, this, nt); @@ -3168,24 +3106,16 @@ void PreSyn::send(double tt, NetCvode* ns, NrnThread* nt) { ns->p[i].interthread_send(tt + delay_, this, nrn_threads + i); } } -#else - ns->event(tt + delay_, this); -#endif -#endif } else { STATISTICS(presyn_send_direct_); for (const auto& d: dil_) { if (d->active_ && d->target_) { NrnThread* n = PP2NT(d->target_); -#if BBTQ == 5 if (nt == n) { ns->bin_event(tt + d->delay_, d, n); } else { ns->p[n->id].interthread_send(tt + d->delay_, d, n); } -#else - ns->event(tt + d->delay_, d, PP2NT(d->target_)); -#endif } } } @@ -6006,7 +5936,6 @@ void NetCvode::deliver_net_events(NrnThread* nt) { // for default method int tid = nt->id; tsav = nt->_t; tm = nt->_t + 0.5 * nt->_dt; -#if BBTQ == 5 tryagain: // one of the events on the main queue may be a NetParEvent // which due to dt round off error can result in an event @@ -6044,18 +5973,15 @@ void NetCvode::deliver_net_events(NrnThread* nt) { // for default method } // assert(int(tm/nt->_dt)%1000 == p[tid].tqe_->nshift_); } -#endif deliver_events(tm, nt); -#if BBTQ == 5 if (nrn_use_bin_queue_) { if (p[tid].tqe_->top()) { goto tryagain; } p[tid].tqe_->shift_bin(tm); } -#endif nt->_t = tsav; } diff --git a/src/nrncvode/netcvode.h b/src/nrncvode/netcvode.h index afddcf998e..eba9eafec9 100644 --- a/src/nrncvode/netcvode.h +++ b/src/nrncvode/netcvode.h @@ -86,12 +86,7 @@ class NetCvode { void move_event(TQItem*, double, NrnThread*); void remove_event(TQItem*, int threadid); TQItem* event(double tdeliver, DiscreteEvent*, NrnThread*); -#if BBTQ == 4 - TQItem* fifo_event(double tdeliver, DiscreteEvent*, NrnThread*); -#endif -#if BBTQ == 5 TQItem* bin_event(double tdeliver, DiscreteEvent*, NrnThread*); -#endif void send2thread(double, DiscreteEvent*, NrnThread*); void null_event(double); void tstop_event(double); diff --git a/src/nrncvode/spt2queue.cpp b/src/nrncvode/spt2queue.cpp deleted file mode 100644 index 07dbcab1d1..0000000000 --- a/src/nrncvode/spt2queue.cpp +++ /dev/null @@ -1,272 +0,0 @@ -// splay tree + 2nd splay tree for event-sets or priority queues -// this starts from the sptqueue.cpp file and adds a 2ne splay tree - -/* Derived from David Brower's c translation of pascal code by -Douglas Jones. -*/ -/* The original c code is included from this file but note that instead -of struct _spblk, we are really using TQItem -*/ - -#include -#include -#include -#include - -#define leftlink left_ -#define rightlink right_ -#define uplink parent_ -#define cnt cnt_ -#define key t_ -#include "sptree.h" - -#if 0 -#define sp1enq(i) \ - { \ - i->cnt_ = 0; \ - spenq(i, sptree_); \ - } -#define sp2enq(i) \ - { \ - i->cnt_ = -1; \ - spenq(i, sptree2_); \ - } -#else -#define sp1enq(i) \ - { \ - i->cnt_ = 0; \ - ++nenq1; \ - spenq(i, sptree_); \ - } -#define sp2enq(i) \ - { \ - i->cnt_ = -1; \ - ++nenq2; \ - spenq(i, sptree2_); \ - } -#endif - -TQItem::TQItem() { - left_ = 0; - right_ = 0; - parent_ = 0; -} - -TQItem::~TQItem() {} - -static void deleteitem(TQItem* i) { // only one, semantics changed - assert(i); - tpool_->hpfree(i); -} - -bool TQItem::check() { -#if DOCHECK -#endif - return true; -} - -static void prnt(const TQItem* b, int level) { - int i; - for (i = 0; i < level; ++i) { - Printf(" "); - } - Printf("%g %c %d Q=%p D=%p\n", b->t_, b->data_ ? 'x' : 'o', b->cnt_, b, b->data_); -} - -static void chk(TQItem* b, int level) { - if (!b->check()) { - hoc_execerror("chk failed", errmess_); - } -} - -TQueue::TQueue() { - if (!tpool_) { - tpool_ = new TQItemPool(1000); - } - sptree_ = new SPTREE; - spinit(sptree_); - sptree2_ = new SPTREE; - spinit(sptree2_); - least_ = 0; - -#if COLLECT_TQueue_STATISTICS - nmove = ninsert = nrem = nleast = nbal = ncmplxrem = 0; - nfastmove = ncompare = nleastsrch = nfind = nfindsrch = 0; - nenq1 = nenq2 = 0; -#endif -} - -TQueue::~TQueue() { - TQItem* q; - if (least_) { - deleteitem(least_); - } - while ((q = spdeq(&sptree_->root)) != nullptr) { - deleteitem(q); - } - delete sptree_; - while ((q = spdeq(&sptree2_->root)) != nullptr) { - deleteitem(q); - } - delete sptree2_; -} - -void TQueue::print() { - if (least_) { - prnt(least_, 0); - } - spscan(prnt, static_cast(nullptr), sptree_); - spscan(prnt, static_cast(nullptr), sptree2_); -} - -void TQueue::forall_callback(void (*f)(const TQItem*, int)) { - if (least_) { - f(least_, 0); - } - spscan(f, nullptr, sptree_); - spscan(f, nullptr, sptree2_); -} - -void TQueue::check(const char* mes) {} - -void TQueue::move_least(double tnew) { - TQItem* b = least(); - if (b) { - b->t_ = tnew; - TQItem* nl = sphead(sptree_); - if (nl) { - if (tnew <= nl->t_ && tnew <= q2least_t()) { - ; - } else if (nl->t_ <= q2least_t()) { - least_ = spdeq(&sptree_->root); - sp1enq(b); - } else { - least_ = spdeq(&sptree2_->root); - sp1enq(b); - } - } else if (tnew > q2least_t()) { - least_ = spdeq(&sptree2_->root); - sp1enq(b); - } - } -} - -void TQueue::move(TQItem* i, double tnew) { - STAT(nmove) - if (i == least_) { - move_least(tnew); - } else if (tnew < least_->t_) { - spdelete(i, sptree_); - i->t_ = tnew; - sp1enq(least_); - least_ = i; - } else { - spdelete(i, sptree_); - i->t_ = tnew; - sp1enq(i); - } -} - -void TQueue::statistics() { -#if COLLECT_TQueue_STATISTICS - Printf("insertions=%lu moves=%lu removals=%lu calls to least=%lu\n", - ninsert, - nmove, - nrem, - nleast); - Printf("calls to find=%lu\n", nfind); - Printf("comparisons=%lu\n", sptree_->enqcmps); - Printf("neqn1=%lu nenq2=%lu\n", nenq1, nenq2); -#else - Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.h\n"); -#endif -} - -void TQueue::spike_stat(double* d) { -#if COLLECT_TQueue_STATISTICS - d[0] = ninsert; - d[1] = nmove; - d[2] = nrem; - d[3] = nenq1; - d[4] = nenq2; -#endif -} - -TQItem* TQueue::insert(double t, void* d) { - STAT(ninsert); - TQItem* i = tpool_->alloc(); - i->data_ = d; - i->t_ = t; - i->cnt_ = 0; - if (t < least_t()) { - if (least()) { - sp1enq(least()); - } - least_ = i; - } else { - sp1enq(i); - } - return i; -} - -TQItem* TQueue::insert_fifo(double t, void* d) { - STAT(ninsert); - TQItem* i = tpool_->alloc(); - i->data_ = d; - i->t_ = t; - i->cnt_ = 0; - if (t < least_t()) { - if (least()) { - sp1enq(least()); - } - least_ = i; - } else { - sp2enq(i); - } - return i; -} - -void TQueue::remove(TQItem* q) { - STAT(nrem); - if (q) { - if (q == least_) { - if (sptree_->root) { - TQItem* nl = sphead(sptree_); - if (nl->t_ <= q2least_t()) { - least_ = spdeq(&sptree_->root); - } else { - least_ = spdeq(&sptree2_->root); - } - } else if (sptree2_->root) { - least_ = spdeq(&sptree2_->root); - } else { - least_ = nullptr; - } - } else { - if (q->cnt_ == -1) { - spdelete(q, sptree2_); - } else { - spdelete(q, sptree_); - } - } - tpool_->hpfree(q); - } -} - -TQItem* TQueue::find(double t) { - // search only in the splay tree. if this is a bug then fix it. - STAT(nfind) - if (t == least_t()) { - return least(); - } - TQItem* q; - q = splookup(t, sptree_); - return (q); -} - -double TQueue::q2least_t() { - if (sptree2_->root) { - return sphead(sptree2_)->t_; - } - return 1e50; // must be larger than any possible t -} diff --git a/src/nrncvode/spt2queue.h b/src/nrncvode/spt2queue.h deleted file mode 100644 index d961922b20..0000000000 --- a/src/nrncvode/spt2queue.h +++ /dev/null @@ -1,64 +0,0 @@ -//#ifndef tqueue_h -//#define tqueue_h - -// second splay tree for the NetCons and PreSyns with same delay. First -// splay tree for others (especially SelfEvents). -// forall_callback does one splay tree first and then the other (so -// not in time order) -#define COLLECT_TQueue_STATISTICS 1 -struct SPTREE; - -class TQItem { - public: - TQItem(); - virtual ~TQItem(); - bool check(); - void clear(){}; - - public: - void* data_; - double t_; - TQItem* left_; - TQItem* right_; - TQItem* parent_; - int cnt_; // reused: -1 means it is in the second splay tree -}; - -class TQueue { - public: - TQueue(); - virtual ~TQueue(); - TQItem* least() { - return least_; - } - double least_t() { - if (least_) { - return least_->t_; - } else { - return 1e15; - } - } - TQItem* insert(double t, void* data_); - TQItem* insert_fifo(double t, void* data_); - TQItem* find(double t); - void remove(TQItem*); - void move(TQItem*, double tnew); - void move_least(double tnew); - void print(); - void check(const char* errmess); - void statistics(); - void spike_stat(double*); - void forall_callback(void (*)(const TQItem*, int)); - double q2least_t(); - - private: - SPTREE* sptree_; - SPTREE* sptree2_; - TQItem* least_; -#if COLLECT_TQueue_STATISTICS - unsigned long ninsert, nrem, nleast, nbal, ncmplxrem; - unsigned long ncompare, nleastsrch, nfind, nfindsrch, nmove, nfastmove; - unsigned long nenq1, nenq2; -#endif -}; -//#endif diff --git a/src/nrncvode/sptqueue.cpp b/src/nrncvode/sptqueue.cpp deleted file mode 100644 index b42f0121e8..0000000000 --- a/src/nrncvode/sptqueue.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// splay tree for event-sets or priority queues -/* Derived from David Brower's c translation of pascal code by -Douglas Jones. -*/ -/* The original c code is included from this file but note that instead -of struct _spblk, we are really using TQItem -*/ - -#include -#include -#include -#include - -#define leftlink left_ -#define rightlink right_ -#define uplink parent_ -#define cnt cnt_ -#define key t_ -#include - -TQItem::TQItem() { - left_ = 0; - right_ = 0; - parent_ = 0; -} - -TQItem::~TQItem() {} - -static void deleteitem(TQItem* i) { // only one, semantics changed - assert(i); - tpool_->hpfree(i); -} - -bool TQItem::check() { -#if DOCHECK -#endif - return true; -} - -static void prnt(const TQItem* b, int level) { - int i; - for (i = 0; i < level; ++i) { - Printf(" "); - } - Printf("%g %c %d Q=%p D=%p\n", b->t_, b->data_ ? 'x' : 'o', b->cnt_, b, b->data_); -} - -static void chk(TQItem* b, int level) { - if (!b->check()) { - hoc_execerror("chk failed", errmess_); - } -} - -TQueue::TQueue() { - if (!tpool_) { - tpool_ = new TQItemPool(1000); - } - sptree_ = new SPTREE; - spinit(sptree_); - least_ = 0; - -#if COLLECT_TQueue_STATISTICS - nmove = ninsert = nrem = nleast = nbal = ncmplxrem = 0; - nfastmove = ncompare = nleastsrch = nfind = nfindsrch = 0; -#endif -} - -TQueue::~TQueue() { - TQItem* q; - while ((q = spdeq(&sptree_->root)) != nullptr) { - deleteitem(q); - } - delete sptree_; -} - -void TQueue::print() { - if (least_) { - prnt(least_, 0); - } - spscan(prnt, nullptr, sptree_); -} - -void TQueue::forall_callback(void (*f)(const TQItem*, int)) { - if (least_) { - f(least_, 0); - } - spscan(f, nullptr, sptree_); -} - -void TQueue::check(const char* mes) {} - -TQItem* TQueue::second_least(double t) { - assert(least_); - TQItem* b = sphead(sptree_); - if (b && b->t_ == t) { - return b; - } - return 0; -} - -void TQueue::move_least(double tnew) { - TQItem* b = least(); - if (b) { - b->t_ = tnew; - TQItem* nl = sphead(sptree_); - if (nl) { - if (tnew > nl->t_) { - least_ = spdeq(&sptree_->root); - spenq(b, sptree_); - } - } - } -} - -void TQueue::move(TQItem* i, double tnew) { - STAT(nmove) - if (i == least_) { - move_least(tnew); - } else if (tnew < least_->t_) { - spdelete(i, sptree_); - i->t_ = tnew; - spenq(least_, sptree_); - least_ = i; - } else { - spdelete(i, sptree_); - i->t_ = tnew; - spenq(i, sptree_); - } -} - -void TQueue::statistics() { -#if COLLECT_TQueue_STATISTICS - Printf("insertions=%lu moves=%lu removals=%lu calls to least=%lu\n", - ninsert, - nmove, - nrem, - nleast); - Printf("calls to find=%lu\n", nfind); - Printf("comparisons=%lu\n", sptree_->enqcmps); -#else - Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.h\n"); -#endif -} - -void TQueue::spike_stat(double* d) { -#if COLLECT_TQueue_STATISTICS - d[0] = ninsert; - d[1] = nmove; - d[2] = nrem; -#endif -} - -TQItem* TQueue::insert(double t, void* d) { - STAT(ninsert); - TQItem* i = tpool_->alloc(); - i->data_ = d; - i->t_ = t; - i->cnt_ = 0; - if (t < least_t()) { - if (least()) { - spenq(least(), sptree_); - } - least_ = i; - } else { - spenq(i, sptree_); - } - return i; -} - -void TQueue::remove(TQItem* q) { - STAT(nrem); - if (q) { - if (q == least_) { - if (sptree_->root) { - least_ = spdeq(&sptree_->root); - } else { - least_ = nullptr; - } - } else { - spdelete(q, sptree_); - } - tpool_->hpfree(q); - } -} - -TQItem* TQueue::find(double t) { - STAT(nfind) - if (t == least_t()) { - return least(); - } - TQItem* q; - q = splookup(t, sptree_); - return (q); -} diff --git a/src/nrncvode/sptqueue.h b/src/nrncvode/sptqueue.h deleted file mode 100644 index fbe734ea67..0000000000 --- a/src/nrncvode/sptqueue.h +++ /dev/null @@ -1,57 +0,0 @@ -//#ifndef tqueue_h -//#define tqueue_h - -#define COLLECT_TQueue_STATISTICS 1 -struct SPTREE; - -class TQItem { - public: - TQItem(); - virtual ~TQItem(); - bool check(); - void clear(){}; - - public: - void* data_; - double t_; - TQItem* left_; - TQItem* right_; - TQItem* parent_; - int cnt_; -}; - -class TQueue { - public: - TQueue(); - virtual ~TQueue(); - TQItem* least() { - return least_; - } - double least_t() { - if (least_) { - return least_->t_; - } else { - return 1e15; - } - } - TQItem* second_least(double t); - TQItem* insert(double t, void* data_); - TQItem* find(double t); - void remove(TQItem*); - void move(TQItem*, double tnew); - void move_least(double tnew); - void print(); - void check(const char* errmess); - void statistics(); - void spike_stat(double*); - void forall_callback(void (*)(const TQItem*, int)); - - private: - SPTREE* sptree_; - TQItem* least_; -#if COLLECT_TQueue_STATISTICS - unsigned long ninsert, nrem, nleast, nbal, ncmplxrem; - unsigned long ncompare, nleastsrch, nfind, nfindsrch, nmove, nfastmove; -#endif -}; -//#endif diff --git a/src/nrncvode/sptree.h b/src/nrncvode/sptree.h index 2e5867d600..bd6d7c55b9 100644 --- a/src/nrncvode/sptree.h +++ b/src/nrncvode/sptree.h @@ -283,9 +283,6 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { n->rightlink = temp; } -#if BBTQ != 4 && BBTQ != 5 - n->cnt++; -#endif return (n); } /* spenq */ @@ -512,9 +509,6 @@ void splay(SPBLK* n, SPTREE* q) { SPBLK* left; /* the top of left subtree being built */ SPBLK* right; /* the top of right subtree being built */ -#if BBTQ != 4 && BBTQ != 5 - n->cnt++; /* bump reference count */ -#endif left = n->leftlink; right = n->rightlink; @@ -967,9 +961,6 @@ SPTREE *q; n->leftlink = NULL; n->rightlink = NULL; n->uplink = NULL; -#if BBTQ != 4 && BBTQ != 5 - n->cnt = 0; -#endif spenq( n, q ); } diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 92c7990947..729c6fdb63 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -104,15 +104,7 @@ void TQueue_reg() { //---------------- -#if BBTQ == 0 -#include -#elif BBTQ == 2 -#include -#elif BBTQ == 4 -#include -#elif BBTQ == 5 #include -#endif SelfQueue::SelfQueue(TQItemPool* tp, int mkmut) { MUTCONSTRUCT(mkmut) diff --git a/src/nrncvode/tqueue.h b/src/nrncvode/tqueue.h index e079a7e360..97157fbc31 100644 --- a/src/nrncvode/tqueue.h +++ b/src/nrncvode/tqueue.h @@ -8,18 +8,7 @@ class TQItem; using TQItemPool = MutexPool; -// 0 use bbtqueue, 2 use sptqueue, 4 use spt2queue, 5 use sptbinqueue -#define BBTQ 5 - -#if BBTQ == 0 -#include -#elif BBTQ == 2 -#include -#elif BBTQ == 4 -#include -#elif BBTQ == 5 #include -#endif class SelfQueue { // not really a queue but a doubly linked list for fast public: // insertion, deletion, iteration diff --git a/src/nrniv/netpar.cpp b/src/nrniv/netpar.cpp index 249d577a2b..7951ab2945 100644 --- a/src/nrniv/netpar.cpp +++ b/src/nrniv/netpar.cpp @@ -156,11 +156,7 @@ void ncs_netcon_inject(int srcgid, int netconIndex, double spikeTime, bool local NetCon* d = ps->dil_.item(netconIndex); NrnThread* nt = nrn_threads; if (d->active_ && d->target_) { -#if BBTQ == 5 ns->bin_event(spikeTime + d->delay_, d, nt); -#else - ns->event(spikeTime + d->delay_, d, nt); -#endif } } From ee736d4921f808ae3dd29998a21c6d276f33aa89 Mon Sep 17 00:00:00 2001 From: nrnhines Date: Mon, 19 Feb 2024 08:22:59 -0500 Subject: [PATCH 09/26] iv update: allows build types Fast and FastDebug. (#2747) --- external/iv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/iv b/external/iv index 2b01af6a55..67b6b20ed8 160000 --- a/external/iv +++ b/external/iv @@ -1 +1 @@ -Subproject commit 2b01af6a55a3935575f136c3cd16e6e1f873a0a7 +Subproject commit 67b6b20ed8784da3a8070cc1042f65e11cdf8b08 From f23b424b76c340024436f39f56984728f00122f0 Mon Sep 17 00:00:00 2001 From: nrnhines Date: Mon, 19 Feb 2024 10:01:01 -0500 Subject: [PATCH 10/26] Fix cvode.use_fast_imem(1) error with electrode time varying conductance. (#2733) --------- Co-authored-by: Pramod Kumbhar --- src/coreneuron/sim/treeset_core.cpp | 10 +- src/nrnoc/treeset.cpp | 10 +- test/pytest_coreneuron/test_imem.py | 171 ++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 10 deletions(-) create mode 100644 test/pytest_coreneuron/test_imem.py diff --git a/src/coreneuron/sim/treeset_core.cpp b/src/coreneuron/sim/treeset_core.cpp index 208058fe1a..c99ede4f1c 100644 --- a/src/coreneuron/sim/treeset_core.cpp +++ b/src/coreneuron/sim/treeset_core.cpp @@ -70,8 +70,8 @@ static void nrn_rhs(NrnThread* _nt) { } if (_nt->nrn_fast_imem) { - /* _nrn_save_rhs has only the contribution of electrode current - so here we transform so it only has membrane current contribution + /* nrn_sav_rhs has only the contribution of electrode current + here we transform so it only has membrane current contribution */ double* p = _nt->nrn_fast_imem->nrn_sav_rhs; nrn_pragma_acc(parallel loop present(p, vec_rhs) if (_nt->compute_gpu) @@ -148,14 +148,14 @@ static void nrn_lhs(NrnThread* _nt) { int* parent_index = _nt->_v_parent_index; if (_nt->nrn_fast_imem) { - /* _nrn_save_d has only the contribution of electrode current - so here we transform so it only has membrane current contribution + /* nrn_sav_d has only the contribution of electrode current + here we transform so it only has membrane current contribution */ double* p = _nt->nrn_fast_imem->nrn_sav_d; nrn_pragma_acc(parallel loop present(p, vec_d) if (_nt->compute_gpu) async(_nt->stream_id)) nrn_pragma_omp(target teams distribute parallel for if(_nt->compute_gpu)) for (int i = i1; i < i3; ++i) { - p[i] += vec_d[i]; + p[i] = vec_d[i] - p[i]; } } diff --git a/src/nrnoc/treeset.cpp b/src/nrnoc/treeset.cpp index 0849d1cdb5..16642afaa7 100644 --- a/src/nrnoc/treeset.cpp +++ b/src/nrnoc/treeset.cpp @@ -436,8 +436,8 @@ void nrn_rhs(neuron::model_sorted_token const& cache_token, NrnThread& nt) { activsynapse_rhs(); if (vec_sav_rhs) { - /* _nrn_save_rhs has only the contribution of electrode current - so here we transform so it only has membrane current contribution + /* vec_sav_rhs has only the contribution of electrode current + here we transform so it only has membrane current contribution */ for (i = i1; i < i3; ++i) { vec_sav_rhs[i] -= vec_rhs[i]; @@ -541,11 +541,11 @@ void nrn_lhs(neuron::model_sorted_token const& sorted_token, NrnThread& nt) { activsynapse_lhs(); if (vec_sav_d) { - /* _nrn_save_d has only the contribution of electrode current - so here we transform so it only has membrane current contribution + /* vec_sav_d has only the contribution of electrode current + here we transform so it only has membrane current contribution */ for (i = i1; i < i3; ++i) { - vec_sav_d[i] += vec_d[i]; + vec_sav_d[i] = vec_d[i] - vec_sav_d[i]; } } #if EXTRACELLULAR diff --git a/test/pytest_coreneuron/test_imem.py b/test/pytest_coreneuron/test_imem.py new file mode 100644 index 0000000000..902dc61782 --- /dev/null +++ b/test/pytest_coreneuron/test_imem.py @@ -0,0 +1,171 @@ +# i_membrane + electrode current sums to 0 when electrode parameters are time dependent. +# This test revealed a fast_imem bug that needed fixing #2733 + +from neuron import h, gui, config +from neuron import coreneuron +import math + +pc = h.ParallelContext() + + +def chkvec(v, str, tol=1e-10): + x = v.c().abs().max() + # print(str, " ", x) + assert math.isclose(x, 0.0, abs_tol=tol) + + +def seclamp(s): + stim = h.SEClamp(s(0.5)) + stim.amp1 = -65 + stim.dur1 = 1 + stim.amp2 = -10 + stim.dur2 = 100 + stim.rs = 10 + return stim + + +def iclamp(s): + stim = h.IClamp(s(0.5)) + stim.delay = 1.0 + stim.dur = 0.1 + stim.amp = 0.3 + return stim + + +def triangle_stim(stimref, step, min, max): + tstim = h.Vector(20).indgen(step) + istim = tstim.c().fill(min) + for i in range(1, len(istim), 2): + istim[i] = max + istim.play(stimref, tstim, 1) + return tstim, istim + + +def tst(xtrcell, secondorder, cvactive, corenrn, seclmp): + # print(xtrcell, secondorder, cvactive, corenrn, seclmp) + cv = h.cvode + cv.use_fast_imem(1) + h.secondorder = secondorder + cv.active(cvactive) + + s = h.Section("s") + s.L = 10 + s.diam = 10 + s.nseg = 1 + s.insert("hh") + + imvec = None + if xtrcell: + s.insert("extracellular") + imvec = h.Vector().record(s(0.5)._ref_i_membrane) + + vstim = None + rstim = None + if seclmp: + stim = seclamp(s) + stim.dur1 = 100 + vstim = triangle_stim(stim._ref_amp1, 0.5, -65, -10) + rstim = triangle_stim(stim._ref_rs, 1, 1000, 100) + else: + stim = iclamp(s) + stim.dur = 100 + vstim = triangle_stim(stim._ref_amp, 0.5, 0, 0.001) + + tvec = h.Vector().record(h._ref_t) + vvec = h.Vector().record(s(0.5)._ref_v) + istimvec = h.Vector().record(stim._ref_i) + imemvec = h.Vector().record(s(0.5)._ref_i_membrane_) + if hasattr(stim, "vc"): + vstimvec = h.Vector().record(stim._ref_vc) + rsstimvec = h.Vector().record(stim._ref_rs) + + glist = [] + + def fstim(): + if h.cvode.active(): + return istimvec.c() + # current balance and imembrane calculation takes place at t + .5*dt + # SEClamp conductance is evaluated (Vector.play) at t + .5*dt + # But recording is at time t + dt. + # So use rsstimvec to evaluate clamp resistance at midpoint + # of each step. That is the resistance for the entire step. + rs = rsstimvec.c().add(rsstimvec.c().rotate(1)).mul(0.5) + rs[0] = rsstimvec[0] + vc = vstimvec.c() + v = vvec.c() + if h.secondorder: + v = vvec.c().add(vvec.c().rotate(1)).mul(0.5) + v[0] = vvec[0] + s = vc.sub(v).div(rs) + return s + + def plt(vec, vecstr): + return # comment out if want to see plots + g = h.Graph() + glist.append(g) + vec.label(vecstr) + vec.line(g, tvec) + g.exec_menu("View = plot") + g.exec_menu("10% Zoom out") + return g + + # run + # h.steps_per_ms = 400 + h.tstop = 10 + pc.set_maxstep(10) + h.finitialize(-65) + if corenrn: + coreneuron.enable = True + coreneuron.verbose = 0 + pc.psolve(h.tstop) + coreneuron.enable = False + coreneuron.verbose = 2 + else: + h.run() + plt(vvec, "v") + plt(imemvec, "i_membrane_") + plt(istimvec, "stim.i") + tmp = (imemvec.c().sub(istimvec), "i_membrane_ - stim_i") + plt(*tmp) + if cv.active() or not seclmp: + chkvec(*tmp) + if xtrcell: + pass + plt(imvec, "i_membrane") + plt(imvec.c().mul(s(0.5).area() / 100).sub(istimvec), "i_membrane - stim_i") + tmp = ( + imvec.c().mul(s(0.5).area() / 100).sub(imemvec), + "i_membrane - i_membrane_", + ) + plt(*tmp) + chkvec(*tmp) + if hasattr(stim, "vc"): + pass + g = plt(rsstimvec, "stim.rs") + if g: + rstim[1].line(g, rstim[0], 2, 1) + plt(vstimvec, "stim.vc") + tmp = (imemvec.c().sub(fstim()), "i_membrane_ - fstim()") + plt(*tmp) + chkvec(*tmp) + + cv.use_fast_imem(0) + h.secondorder = 0 + return glist + + +def test_imem(): + # tst(xtrcell, secondorder, cvactive, corenrn, seclmp) + cnlist = [0] + if config.arguments["NRN_ENABLE_CORENEURON"]: + cnlist.append(1) + for seclmp in [0, 1]: + tst(1, 0, 0, 0, seclmp) + tst(0, 0, 1, 0, seclmp) + for secondorder in [0, 2]: + for corenrn in cnlist: + tst(0, secondorder, 0, corenrn, seclmp) + + +if __name__ == "__main__": + test_imem() From 1049086451138a6e7ac7b61022f74ca1f487d5b6 Mon Sep 17 00:00:00 2001 From: Pramod Kumbhar Date: Wed, 21 Feb 2024 16:31:27 +0100 Subject: [PATCH 11/26] Fix master CI: check if coreneuron is loadable for given test (#2753) * Fix master CI: check if coreneuron is loadable for given test - In order to run python tests with coreneuron, we need to either use special (where coreneuron is linked) or have shared library build so that coreneuron can be loaded dynamically - Some builds on gitlab also tests build with static libraries. So the test added in #2733 fails as we don't check if static builds - In this PR we use existing mechanism/function to check if coreneuron is usable and then run test with coreneuron --- .../tests/utils/coreneuron_available.py | 27 +++++++++++++++++++ test/pytest_coreneuron/test_fast_imem.py | 23 +--------------- test/pytest_coreneuron/test_imem.py | 4 ++- test/pytest_coreneuron/test_multigid.py | 25 ++--------------- 4 files changed, 33 insertions(+), 46 deletions(-) create mode 100644 share/lib/python/neuron/tests/utils/coreneuron_available.py diff --git a/share/lib/python/neuron/tests/utils/coreneuron_available.py b/share/lib/python/neuron/tests/utils/coreneuron_available.py new file mode 100644 index 0000000000..48b30b6d99 --- /dev/null +++ b/share/lib/python/neuron/tests/utils/coreneuron_available.py @@ -0,0 +1,27 @@ +# Check if coreneuron is enabled in the current installation +# and loadable at runtime. This may not be the case if we +# build static library of the coreneuron. + +from neuron import config, h + + +def coreneuron_available(): + if not config.arguments["NRN_ENABLE_CORENEURON"]: + return False + # But can it be loaded? + cvode = h.CVode() + pc = h.ParallelContext() + h.finitialize() + result = 0 + import sys + from io import StringIO + + original_stderr = sys.stderr + sys.stderr = StringIO() + try: + pc.nrncore_run("--tstop 1 --verbose 0") + result = 1 + except Exception as e: + pass + sys.stderr = original_stderr + return result diff --git a/test/pytest_coreneuron/test_fast_imem.py b/test/pytest_coreneuron/test_fast_imem.py index 6570cc9ea0..ea50246db2 100644 --- a/test/pytest_coreneuron/test_fast_imem.py +++ b/test/pytest_coreneuron/test_fast_imem.py @@ -2,6 +2,7 @@ # For a demanding test, use a tree with many IClamp and ExpSyn point processes # sprinkled on zero and non-zero area nodes. from neuron.tests.utils.strtobool import strtobool +from neuron.tests.utils.coreneuron_available import coreneuron_available import os from neuron import config, gui, h @@ -209,28 +210,6 @@ def test_fastimem(): run(1.0, ics, 1e-12) -def coreneuron_available(): - if not config.arguments["NRN_ENABLE_CORENEURON"]: - return False - # But can it be loaded? - cvode = h.CVode() - pc = h.ParallelContext() - h.finitialize() - result = 0 - import sys - from io import StringIO - - original_stderr = sys.stderr - sys.stderr = StringIO() - try: - pc.nrncore_run("--tstop 1 --verbose 0") - result = 1 - except Exception as e: - pass - sys.stderr = original_stderr - return result - - def test_fastimem_corenrn(): ncell = 5 tstop = 1.0 diff --git a/test/pytest_coreneuron/test_imem.py b/test/pytest_coreneuron/test_imem.py index 902dc61782..ceb4fdd2ad 100644 --- a/test/pytest_coreneuron/test_imem.py +++ b/test/pytest_coreneuron/test_imem.py @@ -1,6 +1,8 @@ # i_membrane + electrode current sums to 0 when electrode parameters are time dependent. # This test revealed a fast_imem bug that needed fixing #2733 +from neuron.tests.utils.coreneuron_available import coreneuron_available + from neuron import h, gui, config from neuron import coreneuron import math @@ -157,7 +159,7 @@ def plt(vec, vecstr): def test_imem(): # tst(xtrcell, secondorder, cvactive, corenrn, seclmp) cnlist = [0] - if config.arguments["NRN_ENABLE_CORENEURON"]: + if coreneuron_available(): cnlist.append(1) for seclmp in [0, 1]: tst(1, 0, 0, 0, seclmp) diff --git a/test/pytest_coreneuron/test_multigid.py b/test/pytest_coreneuron/test_multigid.py index 18e66efd53..19008f8461 100644 --- a/test/pytest_coreneuron/test_multigid.py +++ b/test/pytest_coreneuron/test_multigid.py @@ -2,6 +2,8 @@ # pc.cell(gid, h.NetCon(cell.sec(x)._ref_v, None, sec=cell.sec)) # It is an error to have multiple gids with the same reference. +from neuron.tests.utils.coreneuron_available import coreneuron_available + from neuron import config, h, coreneuron from neuron.expect_hocerr import expect_err from math import log10 @@ -9,29 +11,6 @@ pc = h.ParallelContext() - -def coreneuron_available(): - if not config.arguments["NRN_ENABLE_CORENEURON"]: - return False - # But can it be loaded? - cvode = h.CVode() - pc = h.ParallelContext() - h.finitialize() - result = 0 - import sys - from io import StringIO - - original_stderr = sys.stderr - sys.stderr = StringIO() - try: - pc.nrncore_run("--tstop 1 --verbose 0") - result = 1 - except Exception as e: - pass - sys.stderr = original_stderr - return result - - cn_avail = coreneuron_available() From 59c8db0c0509f9959d64dffeea0cea82582561da Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Wed, 21 Feb 2024 18:38:49 +0100 Subject: [PATCH 12/26] Move sptbinq code into tqueue (#2750) --- src/nrncvode/sptbinq.cpp | 395 ------------------------------------- src/nrncvode/sptbinq.h | 155 --------------- src/nrncvode/tqueue.cpp | 397 +++++++++++++++++++++++++++++++++++++- src/nrncvode/tqueue.h | 157 ++++++++++++++- src/nrniv/bbsavestate.cpp | 2 +- 5 files changed, 549 insertions(+), 557 deletions(-) delete mode 100644 src/nrncvode/sptbinq.cpp delete mode 100644 src/nrncvode/sptbinq.h diff --git a/src/nrncvode/sptbinq.cpp b/src/nrncvode/sptbinq.cpp deleted file mode 100644 index 49acfc4a0b..0000000000 --- a/src/nrncvode/sptbinq.cpp +++ /dev/null @@ -1,395 +0,0 @@ -// splay tree + bin queue limited to fixed step method -// for event-sets or priority queues -// this starts from the sptqueue.cpp file and adds a bin queue - -/* Derived from David Brower's c translation of pascal code by -Douglas Jones. -*/ -/* The original c code is included from this file but note that instead -of struct _spblk, we are really using TQItem -*/ - -#include -#include -#include -#include -#include - -#define leftlink left_ -#define rightlink right_ -#define uplink parent_ -#define cnt cnt_ -#define key t_ -#include - -// extern double dt; -#define nt_dt nrn_threads->_dt - -void (*nrn_binq_enqueue_error_handler)(double, TQItem*); - -TQItem::TQItem() { - left_ = 0; - right_ = 0; - parent_ = 0; -} - -TQItem::~TQItem() {} - -bool TQItem::check() { -#if DOCHECK -#endif - return true; -} - -static void prnt(const TQItem* b, int level) { - int i; - for (i = 0; i < level; ++i) { - Printf(" "); - } - Printf("%g %c %d Q=%p D=%p\n", b->t_, b->data_ ? 'x' : 'o', b->cnt_, b, b->data_); -} - -static void chk(TQItem* b, int level) { - if (!b->check()) { - hoc_execerror("chk failed", errmess_); - } -} - -TQueue::TQueue(TQItemPool* tp, int mkmut) { - MUTCONSTRUCT(mkmut) - tpool_ = tp; - nshift_ = 0; - sptree_ = new SPTREE; - spinit(sptree_); - binq_ = new BinQ; - least_ = 0; - -#if COLLECT_TQueue_STATISTICS - nmove = ninsert = nrem = nleast = nbal = ncmplxrem = 0; - nfastmove = ncompare = nleastsrch = nfind = nfindsrch = 0; -#endif -} - -TQueue::~TQueue() { - TQItem *q, *q2; - while ((q = spdeq(&sptree_->root)) != nullptr) { - deleteitem(q); - } - delete sptree_; - for (q = binq_->first(); q; q = q2) { - q2 = binq_->next(q); - remove(q); - } - delete binq_; - MUTDESTRUCT -} - -void TQueue::deleteitem(TQItem* i) { - tpool_->hpfree(i); -} - -void TQueue::print() { - MUTLOCK - if (least_) { - prnt(least_, 0); - } - spscan(prnt, static_cast(nullptr), sptree_); - for (TQItem* q = binq_->first(); q; q = binq_->next(q)) { - prnt(q, 0); - } - MUTUNLOCK -} - -void TQueue::forall_callback(void (*f)(const TQItem*, int)) { - MUTLOCK - if (least_) { - f(least_, 0); - } - spscan(f, static_cast(nullptr), sptree_); - for (TQItem* q = binq_->first(); q; q = binq_->next(q)) { - f(q, 0); - } - MUTUNLOCK -} - -void TQueue::check(const char* mes) {} - -// for Parallel Global Variable Timestep method. -// Assume not using bin queue. -TQItem* TQueue::second_least(double t) { - assert(least_); - TQItem* b = sphead(sptree_); - if (b && b->t_ == t) { - return b; - } - return 0; -} - -void TQueue::move_least(double tnew) { - MUTLOCK - move_least_nolock(tnew); - MUTUNLOCK -} - -void TQueue::move_least_nolock(double tnew) { - TQItem* b = least(); - if (b) { - b->t_ = tnew; - TQItem* nl = sphead(sptree_); - if (nl) { - if (tnew > nl->t_) { - least_ = spdeq(&sptree_->root); - spenq(b, sptree_); - } - } - } -} - -void TQueue::move(TQItem* i, double tnew) { - MUTLOCK - STAT(nmove) - if (i == least_) { - move_least_nolock(tnew); - } else if (tnew < least_->t_) { - spdelete(i, sptree_); - i->t_ = tnew; - spenq(least_, sptree_); - least_ = i; - } else { - spdelete(i, sptree_); - i->t_ = tnew; - spenq(i, sptree_); - } - MUTUNLOCK -} - -void TQueue::statistics() { -#if COLLECT_TQueue_STATISTICS - Printf("insertions=%lu moves=%lu removals=%lu calls to least=%lu\n", - ninsert, - nmove, - nrem, - nleast); - Printf("calls to find=%lu\n", nfind); - Printf("comparisons=%d\n", sptree_->enqcmps); -#else - Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.h\n"); -#endif -} - -void TQueue::spike_stat(double* d) { -#if COLLECT_TQueue_STATISTICS - d[0] = ninsert; - d[1] = nmove; - d[2] = nrem; -// printf("FifoQ spikestat nfenq=%lu nfdeq=%lu nfrem=%lu\n", fifo_->nfenq, fifo_->nfdeq, -// fifo_->nfrem); -#endif -} - -TQItem* TQueue::insert(double t, void* d) { - MUTLOCK - STAT(ninsert); - TQItem* i = tpool_->alloc(); - i->data_ = d; - i->t_ = t; - i->cnt_ = -1; - if (t < least_t_nolock()) { - if (least()) { - spenq(least(), sptree_); - } - least_ = i; - } else { - spenq(i, sptree_); - } - MUTUNLOCK - return i; -} - -TQItem* TQueue::enqueue_bin(double td, void* d) { - MUTLOCK - STAT(ninsert); - TQItem* i = tpool_->alloc(); - i->data_ = d; - i->t_ = td; - binq_->enqueue(td, i); - MUTUNLOCK - return i; -} - -void TQueue::release(TQItem* q) { - // if lockable then the pool is internally handles locking - tpool_->hpfree(q); -} - -void TQueue::remove(TQItem* q) { - MUTLOCK - STAT(nrem); - if (q) { - if (q == least_) { - if (sptree_->root) { - least_ = spdeq(&sptree_->root); - } else { - least_ = nullptr; - } - } else if (q->cnt_ >= 0) { - binq_->remove(q); - } else { - spdelete(q, sptree_); - } - tpool_->hpfree(q); - } - MUTUNLOCK -} - -TQItem* TQueue::atomic_dq(double tt) { - TQItem* q = 0; - MUTLOCK - if (least_ && least_->t_ <= tt) { - q = least_; - STAT(nrem); - if (sptree_->root) { - least_ = spdeq(&sptree_->root); - } else { - least_ = nullptr; - } - } - MUTUNLOCK - return q; -} - -TQItem* TQueue::find(double t) { - TQItem* q; - MUTLOCK - // search only in the splay tree. if this is a bug then fix it. - STAT(nfind) - if (t == least_t_nolock()) { - q = least(); - } else { - q = splookup(t, sptree_); - } - MUTUNLOCK - return (q); -} - -BinQ::BinQ() { - nbin_ = 1000; - bins_ = new TQItem*[nbin_]; - for (int i = 0; i < nbin_; ++i) { - bins_[i] = 0; - } - qpt_ = 0; - tt_ = 0.; -#if COLLECT_TQueue_STATISTICS - nfenq = nfdeq = nfrem = 0; -#endif -} - -BinQ::~BinQ() { - for (int i = 0; i < nbin_; ++i) { - assert(!bins_[i]); - } - delete[] bins_; -} - -void BinQ::resize(int size) { - // printf("BinQ::resize from %d to %d\n", nbin_, size); - int i, j; - TQItem* q; - assert(size >= nbin_); - TQItem** bins = new TQItem*[size]; - for (i = nbin_; i < size; ++i) { - bins[i] = 0; - } - for (i = 0, j = qpt_; i < nbin_; ++i, ++j) { - if (j >= nbin_) { - j = 0; - } - bins[i] = bins_[j]; - for (q = bins[i]; q; q = q->left_) { - q->cnt_ = i; - } - } - delete[] bins_; - bins_ = bins; - nbin_ = size; - qpt_ = 0; -} -void BinQ::enqueue(double td, TQItem* q) { - int idt = (int) ((td - tt_) / nt_dt + 1.e-10); - if (idt < 0) { - if (nrn_binq_enqueue_error_handler) { - (*nrn_binq_enqueue_error_handler)(td, q); - return; - } else { - assert(idt >= 0); - } - } - if (idt >= nbin_) { - resize(idt + 100); - } - // assert (idt < nbin_); - idt += qpt_; - if (idt >= nbin_) { - idt -= nbin_; - } - // printf("enqueue idt=%d qpt=%d\n", idt, qpt_); - assert(idt < nbin_); - q->cnt_ = idt; // only for iteration - q->left_ = bins_[idt]; - bins_[idt] = q; -#if COLLECT_TQueue_STATISTICS - ++nfenq; -#endif -} -TQItem* BinQ::dequeue() { - TQItem* q = bins_[qpt_]; - if (q) { - bins_[qpt_] = q->left_; -#if COLLECT_TQueue_STATISTICS - ++nfdeq; -#endif - } - return q; -} - -/** Iterate in ascending bin order starting at current bin **/ -TQItem* BinQ::first() { - for (int i = 0; i < nbin_; ++i) { - // start at least time qpt_ up to nbin_, and then wrap - // around to 0 and go up to qpt_ - int j = (qpt_ + i) % nbin_; - if (bins_[j]) { - return bins_[j]; - } - } - return 0; -} -TQItem* BinQ::next(TQItem* q) { - if (q->left_) { - return q->left_; - } - // next non-empty bin starting at q->cnt_ + 1, until reach - // exactly qpt_, possibly wrapping around back to 0 if reach nbin_ - for (int i = (q->cnt_ + 1) % nbin_; i != qpt_; i = (i + 1) % nbin_) { - if (bins_[i]) { - return bins_[i]; - } - } - return 0; -} - -void BinQ::remove(TQItem* q) { - TQItem *q1, *q2; - q1 = bins_[q->cnt_]; - if (q1 == q) { - bins_[q->cnt_] = q->left_; - return; - } - for (q2 = q1->left_; q2; q1 = q2, q2 = q2->left_) { - if (q2 == q) { - q1->left_ = q->left_; - return; - } - } -} diff --git a/src/nrncvode/sptbinq.h b/src/nrncvode/sptbinq.h deleted file mode 100644 index f625a72a33..0000000000 --- a/src/nrncvode/sptbinq.h +++ /dev/null @@ -1,155 +0,0 @@ -//#ifndef tqueue_h -//#define tqueue_h - -// bin queue for the fixed step method for NetCons and PreSyns. Splay tree -// for others. -// fifo for the NetCons and PreSyns with same delay. Splay tree for -// others (especially SelfEvents). -// note that most methods below assume a TQItem is in the splay tree -// For the bin part, only insert_fifo, and remove make sense, -// and forall_callback does the splay tree first and then the bin (so -// not in time order) -// The bin part assumes a fixed step method. - -#include - -#define COLLECT_TQueue_STATISTICS 1 -template -struct SPTREE; - -class TQItem { - public: - TQItem(); - virtual ~TQItem(); - bool check(); - void clear(){}; - - public: - void* data_; - double t_; - TQItem* left_; - TQItem* right_; - TQItem* parent_; - int cnt_; // reused: -1 means it is in the splay tree, >=0 gives bin -}; - -// helper class for the TQueue (SplayTBinQueue). -class BinQ { - public: - BinQ(); - virtual ~BinQ(); - void enqueue(double tt, TQItem*); - void shift(double tt) { - assert(!bins_[qpt_]); - tt_ = tt; - if (++qpt_ >= nbin_) { - qpt_ = 0; - } - } - TQItem* top() { - return bins_[qpt_]; - } - TQItem* dequeue(); - double tbin() { - return tt_; - } - // for iteration - TQItem* first(); - TQItem* next(TQItem*); - void remove(TQItem*); - void resize(int); -#if COLLECT_TQueue_STATISTICS - public: - int nfenq, nfdeq, nfrem; -#endif - private: - double tt_; // time at beginning of qpt_ interval - int nbin_, qpt_; - TQItem** bins_; -}; - -class TQueue { - public: - TQueue(TQItemPool*, int mkmut = 0); - virtual ~TQueue(); - - TQItem* least() { - return least_; - } - TQItem* second_least(double t); -#if NRN_ENABLE_THREADS - double least_t() { - double tt; - MUTLOCK; - if (least_) { - tt = least_->t_; - } else { - tt = 1e15; - } - MUTUNLOCK; - return tt; - } -#else - double least_t() { - if (least_) { - return least_->t_; - } else { - return 1e15; - } - } -#endif - TQItem* atomic_dq(double til); - TQItem* insert(double t, void* data); - TQItem* enqueue_bin(double t, void* data); - TQItem* dequeue_bin() { - return binq_->dequeue(); - } - void shift_bin(double t) { - ++nshift_; - binq_->shift(t); - } - double tbin() { - return binq_->tbin(); - } - TQItem* top() { - return binq_->top(); - } - void release(TQItem*); - TQItem* find(double t); - void remove(TQItem*); - void move(TQItem*, double tnew); - void move_least(double tnew); - void print(); - void check(const char* errmess); - void statistics(); - void spike_stat(double*); - void forall_callback(void (*)(const TQItem*, int)); - int nshift_; - void deleteitem(TQItem*); - - public: - BinQ* binq() { - return binq_; - } - - private: - double least_t_nolock() { - if (least_) { - return least_->t_; - } else { - return 1e15; - } - } - void move_least_nolock(double tnew); - SPTREE* sptree_; - BinQ* binq_; - TQItem* least_; - TQItemPool* tpool_; - MUTDEC -#if COLLECT_TQueue_STATISTICS - unsigned long ninsert, nrem, nleast, nbal, ncmplxrem; - unsigned long ncompare, nleastsrch, nfind, nfindsrch, nmove, nfastmove; -#endif -}; - -//#endif diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 729c6fdb63..1afb4be6c6 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -1,4 +1,11 @@ #include <../../nrnconf.h> + +#include +#include +#include +#include +#include + #include "tqueue.h" #include "pool.h" @@ -104,7 +111,395 @@ void TQueue_reg() { //---------------- -#include +// splay tree + bin queue limited to fixed step method +// for event-sets or priority queues +// this starts from the sptqueue.cpp file and adds a bin queue + +/* Derived from David Brower's c translation of pascal code by +Douglas Jones. +*/ +/* The original c code is included from this file but note that instead +of struct _spblk, we are really using TQItem +*/ + +#define leftlink left_ +#define rightlink right_ +#define uplink parent_ +#define cnt cnt_ +#define key t_ +#include + +// extern double dt; +#define nt_dt nrn_threads->_dt + +void (*nrn_binq_enqueue_error_handler)(double, TQItem*); + +TQItem::TQItem() { + left_ = 0; + right_ = 0; + parent_ = 0; +} + +TQItem::~TQItem() {} + +bool TQItem::check() { +#if DOCHECK +#endif + return true; +} + +static void prnt(const TQItem* b, int level) { + int i; + for (i = 0; i < level; ++i) { + Printf(" "); + } + Printf("%g %c %d Q=%p D=%p\n", b->t_, b->data_ ? 'x' : 'o', b->cnt_, b, b->data_); +} + +static void chk(TQItem* b, int level) { + if (!b->check()) { + hoc_execerror("chk failed", errmess_); + } +} + +TQueue::TQueue(TQItemPool* tp, int mkmut) { + MUTCONSTRUCT(mkmut) + tpool_ = tp; + nshift_ = 0; + sptree_ = new SPTREE; + spinit(sptree_); + binq_ = new BinQ; + least_ = 0; + +#if COLLECT_TQueue_STATISTICS + nmove = ninsert = nrem = nleast = nbal = ncmplxrem = 0; + nfastmove = ncompare = nleastsrch = nfind = nfindsrch = 0; +#endif +} + +TQueue::~TQueue() { + TQItem *q, *q2; + while ((q = spdeq(&sptree_->root)) != nullptr) { + deleteitem(q); + } + delete sptree_; + for (q = binq_->first(); q; q = q2) { + q2 = binq_->next(q); + remove(q); + } + delete binq_; + MUTDESTRUCT +} + +void TQueue::deleteitem(TQItem* i) { + tpool_->hpfree(i); +} + +void TQueue::print() { + MUTLOCK + if (least_) { + prnt(least_, 0); + } + spscan(prnt, static_cast(nullptr), sptree_); + for (TQItem* q = binq_->first(); q; q = binq_->next(q)) { + prnt(q, 0); + } + MUTUNLOCK +} + +void TQueue::forall_callback(void (*f)(const TQItem*, int)) { + MUTLOCK + if (least_) { + f(least_, 0); + } + spscan(f, static_cast(nullptr), sptree_); + for (TQItem* q = binq_->first(); q; q = binq_->next(q)) { + f(q, 0); + } + MUTUNLOCK +} + +void TQueue::check(const char* mes) {} + +// for Parallel Global Variable Timestep method. +// Assume not using bin queue. +TQItem* TQueue::second_least(double t) { + assert(least_); + TQItem* b = sphead(sptree_); + if (b && b->t_ == t) { + return b; + } + return 0; +} + +void TQueue::move_least(double tnew) { + MUTLOCK + move_least_nolock(tnew); + MUTUNLOCK +} + +void TQueue::move_least_nolock(double tnew) { + TQItem* b = least(); + if (b) { + b->t_ = tnew; + TQItem* nl = sphead(sptree_); + if (nl) { + if (tnew > nl->t_) { + least_ = spdeq(&sptree_->root); + spenq(b, sptree_); + } + } + } +} + +void TQueue::move(TQItem* i, double tnew) { + MUTLOCK + STAT(nmove) + if (i == least_) { + move_least_nolock(tnew); + } else if (tnew < least_->t_) { + spdelete(i, sptree_); + i->t_ = tnew; + spenq(least_, sptree_); + least_ = i; + } else { + spdelete(i, sptree_); + i->t_ = tnew; + spenq(i, sptree_); + } + MUTUNLOCK +} + +void TQueue::statistics() { +#if COLLECT_TQueue_STATISTICS + Printf("insertions=%lu moves=%lu removals=%lu calls to least=%lu\n", + ninsert, + nmove, + nrem, + nleast); + Printf("calls to find=%lu\n", nfind); + Printf("comparisons=%d\n", sptree_->enqcmps); +#else + Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.h\n"); +#endif +} + +void TQueue::spike_stat(double* d) { +#if COLLECT_TQueue_STATISTICS + d[0] = ninsert; + d[1] = nmove; + d[2] = nrem; +// printf("FifoQ spikestat nfenq=%lu nfdeq=%lu nfrem=%lu\n", fifo_->nfenq, fifo_->nfdeq, +// fifo_->nfrem); +#endif +} + +TQItem* TQueue::insert(double t, void* d) { + MUTLOCK + STAT(ninsert); + TQItem* i = tpool_->alloc(); + i->data_ = d; + i->t_ = t; + i->cnt_ = -1; + if (t < least_t_nolock()) { + if (least()) { + spenq(least(), sptree_); + } + least_ = i; + } else { + spenq(i, sptree_); + } + MUTUNLOCK + return i; +} + +TQItem* TQueue::enqueue_bin(double td, void* d) { + MUTLOCK + STAT(ninsert); + TQItem* i = tpool_->alloc(); + i->data_ = d; + i->t_ = td; + binq_->enqueue(td, i); + MUTUNLOCK + return i; +} + +void TQueue::release(TQItem* q) { + // if lockable then the pool is internally handles locking + tpool_->hpfree(q); +} + +void TQueue::remove(TQItem* q) { + MUTLOCK + STAT(nrem); + if (q) { + if (q == least_) { + if (sptree_->root) { + least_ = spdeq(&sptree_->root); + } else { + least_ = nullptr; + } + } else if (q->cnt_ >= 0) { + binq_->remove(q); + } else { + spdelete(q, sptree_); + } + tpool_->hpfree(q); + } + MUTUNLOCK +} + +TQItem* TQueue::atomic_dq(double tt) { + TQItem* q = 0; + MUTLOCK + if (least_ && least_->t_ <= tt) { + q = least_; + STAT(nrem); + if (sptree_->root) { + least_ = spdeq(&sptree_->root); + } else { + least_ = nullptr; + } + } + MUTUNLOCK + return q; +} + +TQItem* TQueue::find(double t) { + TQItem* q; + MUTLOCK + // search only in the splay tree. if this is a bug then fix it. + STAT(nfind) + if (t == least_t_nolock()) { + q = least(); + } else { + q = splookup(t, sptree_); + } + MUTUNLOCK + return (q); +} + +BinQ::BinQ() { + nbin_ = 1000; + bins_ = new TQItem*[nbin_]; + for (int i = 0; i < nbin_; ++i) { + bins_[i] = 0; + } + qpt_ = 0; + tt_ = 0.; +#if COLLECT_TQueue_STATISTICS + nfenq = nfdeq = nfrem = 0; +#endif +} + +BinQ::~BinQ() { + for (int i = 0; i < nbin_; ++i) { + assert(!bins_[i]); + } + delete[] bins_; +} + +void BinQ::resize(int size) { + // printf("BinQ::resize from %d to %d\n", nbin_, size); + int i, j; + TQItem* q; + assert(size >= nbin_); + TQItem** bins = new TQItem*[size]; + for (i = nbin_; i < size; ++i) { + bins[i] = 0; + } + for (i = 0, j = qpt_; i < nbin_; ++i, ++j) { + if (j >= nbin_) { + j = 0; + } + bins[i] = bins_[j]; + for (q = bins[i]; q; q = q->left_) { + q->cnt_ = i; + } + } + delete[] bins_; + bins_ = bins; + nbin_ = size; + qpt_ = 0; +} +void BinQ::enqueue(double td, TQItem* q) { + int idt = (int) ((td - tt_) / nt_dt + 1.e-10); + if (idt < 0) { + if (nrn_binq_enqueue_error_handler) { + (*nrn_binq_enqueue_error_handler)(td, q); + return; + } else { + assert(idt >= 0); + } + } + if (idt >= nbin_) { + resize(idt + 100); + } + // assert (idt < nbin_); + idt += qpt_; + if (idt >= nbin_) { + idt -= nbin_; + } + // printf("enqueue idt=%d qpt=%d\n", idt, qpt_); + assert(idt < nbin_); + q->cnt_ = idt; // only for iteration + q->left_ = bins_[idt]; + bins_[idt] = q; +#if COLLECT_TQueue_STATISTICS + ++nfenq; +#endif +} +TQItem* BinQ::dequeue() { + TQItem* q = bins_[qpt_]; + if (q) { + bins_[qpt_] = q->left_; +#if COLLECT_TQueue_STATISTICS + ++nfdeq; +#endif + } + return q; +} + +/** Iterate in ascending bin order starting at current bin **/ +TQItem* BinQ::first() { + for (int i = 0; i < nbin_; ++i) { + // start at least time qpt_ up to nbin_, and then wrap + // around to 0 and go up to qpt_ + int j = (qpt_ + i) % nbin_; + if (bins_[j]) { + return bins_[j]; + } + } + return 0; +} +TQItem* BinQ::next(TQItem* q) { + if (q->left_) { + return q->left_; + } + // next non-empty bin starting at q->cnt_ + 1, until reach + // exactly qpt_, possibly wrapping around back to 0 if reach nbin_ + for (int i = (q->cnt_ + 1) % nbin_; i != qpt_; i = (i + 1) % nbin_) { + if (bins_[i]) { + return bins_[i]; + } + } + return 0; +} + +void BinQ::remove(TQItem* q) { + TQItem *q1, *q2; + q1 = bins_[q->cnt_]; + if (q1 == q) { + bins_[q->cnt_] = q->left_; + return; + } + for (q2 = q1->left_; q2; q1 = q2, q2 = q2->left_) { + if (q2 == q) { + q1->left_ = q->left_; + return; + } + } +} SelfQueue::SelfQueue(TQItemPool* tp, int mkmut) { MUTCONSTRUCT(mkmut) diff --git a/src/nrncvode/tqueue.h b/src/nrncvode/tqueue.h index 97157fbc31..334b7fe5c1 100644 --- a/src/nrncvode/tqueue.h +++ b/src/nrncvode/tqueue.h @@ -1,14 +1,163 @@ -#ifndef tqueue_h -#define tqueue_h +#pragma once + #undef check +#include + #include #include class TQItem; using TQItemPool = MutexPool; -#include +// bin queue for the fixed step method for NetCons and PreSyns. Splay tree +// for others. +// fifo for the NetCons and PreSyns with same delay. Splay tree for +// others (especially SelfEvents). +// note that most methods below assume a TQItem is in the splay tree +// For the bin part, only insert_fifo, and remove make sense, +// and forall_callback does the splay tree first and then the bin (so +// not in time order) +// The bin part assumes a fixed step method. + +#define COLLECT_TQueue_STATISTICS 1 +template +struct SPTREE; + +class TQItem { + public: + TQItem(); + virtual ~TQItem(); + bool check(); + void clear(){}; + + public: + void* data_; + double t_; + TQItem* left_; + TQItem* right_; + TQItem* parent_; + int cnt_; // reused: -1 means it is in the splay tree, >=0 gives bin +}; + +// helper class for the TQueue (SplayTBinQueue). +class BinQ { + public: + BinQ(); + virtual ~BinQ(); + void enqueue(double tt, TQItem*); + void shift(double tt) { + assert(!bins_[qpt_]); + tt_ = tt; + if (++qpt_ >= nbin_) { + qpt_ = 0; + } + } + TQItem* top() { + return bins_[qpt_]; + } + TQItem* dequeue(); + double tbin() { + return tt_; + } + // for iteration + TQItem* first(); + TQItem* next(TQItem*); + void remove(TQItem*); + void resize(int); +#if COLLECT_TQueue_STATISTICS + public: + int nfenq, nfdeq, nfrem; +#endif + private: + double tt_; // time at beginning of qpt_ interval + int nbin_, qpt_; + TQItem** bins_; +}; + +class TQueue { + public: + TQueue(TQItemPool*, int mkmut = 0); + virtual ~TQueue(); + + TQItem* least() { + return least_; + } + TQItem* second_least(double t); +#if NRN_ENABLE_THREADS + double least_t() { + double tt; + MUTLOCK; + if (least_) { + tt = least_->t_; + } else { + tt = 1e15; + } + MUTUNLOCK; + return tt; + } +#else + double least_t() { + if (least_) { + return least_->t_; + } else { + return 1e15; + } + } +#endif + TQItem* atomic_dq(double til); + TQItem* insert(double t, void* data); + TQItem* enqueue_bin(double t, void* data); + TQItem* dequeue_bin() { + return binq_->dequeue(); + } + void shift_bin(double t) { + ++nshift_; + binq_->shift(t); + } + double tbin() { + return binq_->tbin(); + } + TQItem* top() { + return binq_->top(); + } + void release(TQItem*); + TQItem* find(double t); + void remove(TQItem*); + void move(TQItem*, double tnew); + void move_least(double tnew); + void print(); + void check(const char* errmess); + void statistics(); + void spike_stat(double*); + void forall_callback(void (*)(const TQItem*, int)); + int nshift_; + void deleteitem(TQItem*); + + public: + BinQ* binq() { + return binq_; + } + + private: + double least_t_nolock() { + if (least_) { + return least_->t_; + } else { + return 1e15; + } + } + void move_least_nolock(double tnew); + SPTREE* sptree_; + BinQ* binq_; + TQItem* least_; + TQItemPool* tpool_; + MUTDEC +#if COLLECT_TQueue_STATISTICS + unsigned long ninsert, nrem, nleast, nbal, ncmplxrem; + unsigned long ncompare, nleastsrch, nfind, nfindsrch, nmove, nfastmove; +#endif +}; class SelfQueue { // not really a queue but a doubly linked list for fast public: // insertion, deletion, iteration @@ -29,5 +178,3 @@ class SelfQueue { // not really a queue but a doubly linked list for fast TQItemPool* tpool_; MUTDEC }; - -#endif diff --git a/src/nrniv/bbsavestate.cpp b/src/nrniv/bbsavestate.cpp index 7633c4a1b5..f0ebe456cd 100644 --- a/src/nrniv/bbsavestate.cpp +++ b/src/nrniv/bbsavestate.cpp @@ -156,7 +156,7 @@ We do not need to worry about bin queueing since it is the delivery time that is enqueueed and that is always in the future. When bin queueing is used, a mechanism is needed to avoid the assertion -error in BinQ::enqueue (see nrncvode/sptbinq.cpp) when the enqueued event +error in BinQ::enqueue (see nrncvode/tqueue.cpp) when the enqueued event has a delivery time earlier than the binq current time. One possibility is to turn off bin queueing and force all events on the standard queue to be on binq boundaries. Another possibility is for bbsavestate to From c3024abd03e44ac1183ef1b54ca10091fd380daf Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Wed, 21 Feb 2024 20:25:22 +0100 Subject: [PATCH 13/26] Save/Restore cache new way (#2752) --- .github/workflows/neuron-ci.yml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/.github/workflows/neuron-ci.yml b/.github/workflows/neuron-ci.yml index 0c3ecc14a5..3c5d8ca9c1 100644 --- a/.github/workflows/neuron-ci.yml +++ b/.github/workflows/neuron-ci.yml @@ -89,7 +89,7 @@ jobs: steps: - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1 + uses: jwlawson/actions-setup-cmake@v2 with: cmake-version : ${{(matrix.config.python_dynamic || matrix.config.build_mode == 'setuptools') && env.DYNAMIC_PYTHON_CMAKE_VERSION || env.DESIRED_CMAKE_VERSION}} @@ -196,23 +196,11 @@ jobs: cat matrix.json echo ----- - # Workaround for https://github.com/actions/cache/issues/92 - - name: Checkout cache action - uses: actions/checkout@v4 - with: - repository: actions/cache - ref: v3 - path: tmp/actions/cache - - - name: Make actions/cache@v3 run even on failure - run: | - sed -i'.bak' -e '/ post-if: /d' tmp/actions/cache/action.yml - - name: Restore compiler cache - uses: ./tmp/actions/cache + uses: actions/cache@v4 with: - path: | - ${{runner.workspace}}/ccache + path: ${{runner.workspace}}/ccache + save-always: true key: ${{matrix.os}}-${{hashfiles('matrix.json')}}-${{github.ref}}-${{github.sha}} restore-keys: | ${{matrix.os}}-${{hashfiles('matrix.json')}}-${{github.ref}}- From 3b7944421f921abb5816748de3c3f8c8b276efdc Mon Sep 17 00:00:00 2001 From: nrnhines Date: Thu, 22 Feb 2024 00:34:33 -0500 Subject: [PATCH 14/26] Fix Python3.12 SyntaxWarning: invalid escape sequence '\.' (#2756) Has already been fixed in release/8.2 --- bin/nrnpyenv.sh.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/nrnpyenv.sh.in b/bin/nrnpyenv.sh.in index 9294cd9e7a..03f41d06f6 100755 --- a/bin/nrnpyenv.sh.in +++ b/bin/nrnpyenv.sh.in @@ -426,7 +426,7 @@ def nrnpylib_mswin(): f = os.popen(cmd) nrn_pylib = None for line in f: - if re.search('ython[a-zA-Z0-9_.]*\.dll', line): + if re.search(r'ython[a-zA-Z0-9_.]*\.dll', line): nrn_pylib = '/'.join(line.split(os.path.sep)).strip() nrnpylib_provenance="cygcheck" return nrn_pylib @@ -474,7 +474,7 @@ def nrnpylib_linux(): if re.search(r'libpython.*\.so', line): print ("# from lsof: %s" % line) nrn_pylib = line.strip() - nrnpylib_provenance = 'lsof search for libpython.*\.so' + nrnpylib_provenance = r'lsof search for libpython.*\.so' return nrn_pylib else: # figure it out from the os path p = os.path.sep.join(os.__file__.split(os.path.sep)[:-1]) From 32c88945e33affc8480a029fd4f5fa1a82535a7f Mon Sep 17 00:00:00 2001 From: Erik Heeren Date: Thu, 22 Feb 2024 06:37:24 +0100 Subject: [PATCH 15/26] Some extra steps when building a new release (#2736) * Link to readthedocs * Update list of people who can build arm64 wheels --- .github/ISSUE_TEMPLATE/release-patch.md | 4 +++- .github/ISSUE_TEMPLATE/release.md | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/release-patch.md b/.github/ISSUE_TEMPLATE/release-patch.md index 9b7bbb4d1b..ee73129c71 100644 --- a/.github/ISSUE_TEMPLATE/release-patch.md +++ b/.github/ISSUE_TEMPLATE/release-patch.md @@ -18,6 +18,8 @@ Pre-release - [ ] Update semantic version in `CMakeLists.txt` - [ ] Update changelog below and agree on it with everyone; then commit it to `docs/changelog` in the cherrypicks PR (copy structure as-is) - [ ] Update `docs/index.rst` accordingly with the new `.pkg` and `.exe` links for `PKG installer` and `Windows Installer` +- [ ] Activate ReadtheDocs for the cherry-pick branch and ensure the documentation builds (when logged in, go to [the versions page](https://readthedocs.org/projects/nrn/versions/) and set the version for your branch to Active and Hidden) +- [ ] Run a test wheel build WITHOUT upload on the cherry-pick branch to ensure all the wheels build ([see details](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-azure)) Sanity checks --- @@ -33,7 +35,7 @@ Releasing - [ ] Build release wheels but WITHOUT upload ([see details](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-azure)) - [ ] Create, test and upload manual artifacts - [ ] MacOS package installer (manual task, ask Michael) - - [ ] arm64 wheels (manual task, check with Alex or Pramod) + - [ ] arm64 wheels (manual task, check with Erik, Goran or Pramod) - [ ] aarch64 wheels (use existing `release/x.y-aarch64` branch for this, see [guide](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-circleci)) - [ ] Publish the `x.y.z` wheels on PyPI; see [wheel publishing instructions](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-azure) - [ ] Once wheels are published, activate the `x.y.z` tag on ReadTheDocs diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index d222995500..0d0721a066 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -19,6 +19,7 @@ Sanity checks - [ ] Create `release/x.y` branch and make sure GitHub, Azure and CircleCI builds pass - [ ] Run [nrn-build-ci](https://github.com/neuronsimulator/nrn-build-ci/actions/workflows/build-neuron.yml) for the respective Azure build; see [Azure drop guide](https://github.com/neuronsimulator/nrn-build-ci#azure-wheels-testing---manual-workflow) - [ ] Activate ReadTheDocs build for `release/x.y` & make it hidden. Check docs are fine after build is done. +- [ ] Run a test wheel build WITHOUT upload for `release/x.y` to ensure all the wheels build ([see details](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-azure)) - [ ] Run BBP Simulation Stack & other relevant tests @@ -32,7 +33,7 @@ Releasing - [ ] Build release wheels but WITHOUT upload ([see details](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-azure)) - [ ] Create, test and upload manual artifacts - [ ] MacOS package installer (manual task, ask Michael) - - [ ] arm64 wheels (manual task, check with Alex or Pramod) + - [ ] arm64 wheels (manual task, check with Erik, Goran or Pramod) - [ ] aarch64 wheels (create a `release/x.y-aarch64` branch for this, see [guide](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-circleci)) - [ ] Publish the `x.y.z` wheels on Pypi; see [wheel publishing instructions](https://nrn.readthedocs.io/en/latest/install/python_wheels.html#publishing-the-wheels-on-pypi-via-azure) - [ ] Once wheels are published, activate the `x.y.z` tag on ReadTheDocs From 8dcb74255ab146ddbc7e4bde3317894f78a2be44 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Thu, 22 Feb 2024 06:37:49 +0100 Subject: [PATCH 16/26] Move header file to .hpp instead of .h (#2755) --- src/coreneuron/network/tqueue.cpp | 2 +- src/nrncvode/cvodeobj.cpp | 2 +- src/nrncvode/hocevent.cpp | 2 +- src/nrncvode/netcon.h | 2 +- src/nrncvode/netcvode.cpp | 4 ++-- src/nrncvode/netcvode.h | 2 +- src/nrncvode/{pool.h => pool.hpp} | 0 src/nrncvode/{sptree.h => sptree.hpp} | 2 +- src/nrncvode/tqueue.cpp | 8 ++++---- src/nrncvode/{tqueue.h => tqueue.hpp} | 2 +- src/nrniv/bbsavestate.cpp | 2 +- src/nrniv/savstate.cpp | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) rename src/nrncvode/{pool.h => pool.hpp} (100%) rename src/nrncvode/{sptree.h => sptree.hpp} (99%) rename src/nrncvode/{tqueue.h => tqueue.hpp} (99%) diff --git a/src/coreneuron/network/tqueue.cpp b/src/coreneuron/network/tqueue.cpp index ae2f323a37..dd09d9edd1 100644 --- a/src/coreneuron/network/tqueue.cpp +++ b/src/coreneuron/network/tqueue.cpp @@ -124,7 +124,7 @@ void BinQ::remove(TQItem* q) { } } -//#include "coreneuron/nrniv/sptree.h" +//#include "coreneuron/nrniv/sptree.hpp" /* * The following code implements the basic operations on diff --git a/src/nrncvode/cvodeobj.cpp b/src/nrncvode/cvodeobj.cpp index 5576c603e1..d0ed078bb1 100644 --- a/src/nrncvode/cvodeobj.cpp +++ b/src/nrncvode/cvodeobj.cpp @@ -21,7 +21,7 @@ extern int hoc_return_type_code; #include "nrndaspk.h" #include "nrniv_mf.h" #include "nrnpy.h" -#include "tqueue.h" +#include "tqueue.hpp" #include "mymath.h" #include "htlist.h" #include diff --git a/src/nrncvode/hocevent.cpp b/src/nrncvode/hocevent.cpp index e594f95a79..5654ea0b7d 100644 --- a/src/nrncvode/hocevent.cpp +++ b/src/nrncvode/hocevent.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/src/nrncvode/netcon.h b/src/nrncvode/netcon.h index e4a29a8bd6..eed9eda2fd 100644 --- a/src/nrncvode/netcon.h +++ b/src/nrncvode/netcon.h @@ -7,7 +7,7 @@ #include "neuron/container/data_handle.hpp" #include "nrnmpi.h" #include "nrnneosm.h" -#include "pool.h" +#include "pool.hpp" #include diff --git a/src/nrncvode/netcvode.cpp b/src/nrncvode/netcvode.cpp index 4c017f54ad..1573d190e4 100644 --- a/src/nrncvode/netcvode.cpp +++ b/src/nrncvode/netcvode.cpp @@ -13,8 +13,8 @@ #include "parse.hpp" #include "cvodeobj.h" #include "hoclist.h" -#include "pool.h" -#include "tqueue.h" +#include "pool.hpp" +#include "tqueue.hpp" #include "ocobserv.h" #include "nrnneosm.h" #include "datapath.h" diff --git a/src/nrncvode/netcvode.h b/src/nrncvode/netcvode.h index eba9eafec9..65d0765887 100644 --- a/src/nrncvode/netcvode.h +++ b/src/nrncvode/netcvode.h @@ -7,7 +7,7 @@ #include "cvodeobj.h" #include "neuron/container/data_handle.hpp" -#include "tqueue.h" +#include "tqueue.hpp" #include #include diff --git a/src/nrncvode/pool.h b/src/nrncvode/pool.hpp similarity index 100% rename from src/nrncvode/pool.h rename to src/nrncvode/pool.hpp diff --git a/src/nrncvode/sptree.h b/src/nrncvode/sptree.hpp similarity index 99% rename from src/nrncvode/sptree.h rename to src/nrncvode/sptree.hpp index bd6d7c55b9..2af27c822a 100644 --- a/src/nrncvode/sptree.h +++ b/src/nrncvode/sptree.hpp @@ -1,5 +1,5 @@ /* -** sptree.h: The following type declarations provide the binary tree +** sptree.hpp: The following type declarations provide the binary tree ** representation of event-sets or priority queues needed by splay trees ** ** assumes that data and datb will be provided by the application diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 1afb4be6c6..9197a8c700 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -6,8 +6,8 @@ #include #include -#include "tqueue.h" -#include "pool.h" +#include "tqueue.hpp" +#include "pool.hpp" #include "classreg.h" #include "nrnoc2iv.h" @@ -127,7 +127,7 @@ of struct _spblk, we are really using TQItem #define uplink parent_ #define cnt cnt_ #define key t_ -#include +#include // extern double dt; #define nt_dt nrn_threads->_dt @@ -280,7 +280,7 @@ void TQueue::statistics() { Printf("calls to find=%lu\n", nfind); Printf("comparisons=%d\n", sptree_->enqcmps); #else - Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.h\n"); + Printf("Turn on COLLECT_TQueue_STATISTICS_ in tqueue.hpp\n"); #endif } diff --git a/src/nrncvode/tqueue.h b/src/nrncvode/tqueue.hpp similarity index 99% rename from src/nrncvode/tqueue.h rename to src/nrncvode/tqueue.hpp index 334b7fe5c1..0e5ce6202b 100644 --- a/src/nrncvode/tqueue.h +++ b/src/nrncvode/tqueue.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include class TQItem; using TQItemPool = MutexPool; diff --git a/src/nrniv/bbsavestate.cpp b/src/nrniv/bbsavestate.cpp index f0ebe456cd..79403af2dd 100644 --- a/src/nrniv/bbsavestate.cpp +++ b/src/nrniv/bbsavestate.cpp @@ -185,7 +185,7 @@ callback to bbss_early when needed. #include "netcon.h" #include "nrniv_mf.h" -#include "tqueue.h" +#include "tqueue.hpp" #include "vrecitem.h" // on mingw, OUT became defined diff --git a/src/nrniv/savstate.cpp b/src/nrniv/savstate.cpp index eeb6ca81bd..f50473cbba 100644 --- a/src/nrniv/savstate.cpp +++ b/src/nrniv/savstate.cpp @@ -10,7 +10,7 @@ #include "ndatclas.h" #include "nrniv_mf.h" -#include "tqueue.h" +#include "tqueue.hpp" #include "netcon.h" #include "vrecitem.h" #include "utils/enumerate.h" From 0cfc93d588b242aa164da4bf24efd115b4d677cd Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Thu, 22 Feb 2024 15:11:02 +0100 Subject: [PATCH 17/26] Remove useless code from sptree (#2757) --- src/nrncvode/sptree.hpp | 632 +++++----------------------------------- src/nrncvode/tqueue.cpp | 4 +- 2 files changed, 77 insertions(+), 559 deletions(-) diff --git a/src/nrncvode/sptree.hpp b/src/nrncvode/sptree.hpp index 2af27c822a..a46fa6327d 100644 --- a/src/nrncvode/sptree.hpp +++ b/src/nrncvode/sptree.hpp @@ -9,70 +9,28 @@ ** with the compare function applied to the addresses of two keys. */ -#ifndef SPTREE_H -#define SPTREE_H +#pragma once -#ifndef NULL -#define NULL 0 -#endif - -#if 0 -#define STRCMP(a, b) ((Sct = *(a) - *(b)) ? Sct : strcmp((a), (b))) -#else #define STRCMP(a, b) (a - b) -#endif - -#if 0 -typedef struct _spblk -{ - struct _spblk * leftlink; - struct _spblk * rightlink; - struct _spblk * uplink; - int cnt; - - char * key; /* formerly time/timetyp */ - char * data; /* formerly aux/auxtype */ - char * datb; -} SPBLK; -#endif template struct SPTREE { SPBLK* root; /* root node */ - /* Statistics, not strictly necessary, but handy for tuning */ - - int lookups; /* number of splookup()s */ - int lkpcmps; /* number of lookup comparisons */ - - int enqs; /* number of spenq()s */ int enqcmps; /* compares in spenq */ - - int splays; - int splayloops; }; -#define spinit sptq_spinit -#define spempty sptq_spempty -#define spenq sptq_spenq -#define spdeq sptq_spdeq -#define spenqprior sptq_spenqprior -#define splay sptq_splay -#define sphead sptq_sphead -#define spdelete sptq_spdelete -#define spnext sptq_spnext -#define spprev sptq_spprev -#define spenqbefore sptq_spenqbefore -#define spenqafter sptq_spenqafter -#define splookup sptq_splookup -/*#define spinstall sptq_spinstall*/ -#define sptail sptq_sptail -#define spscan sptq_spscan -#define sprscan sptq_sprscan -#define spfhead sptq_spfhead -#define spfnext sptq_spfnext -#define spfprev sptq_spfprev -#define spstats sptq_spstats +#define spinit sptq_spinit +#define spempty sptq_spempty +#define spenq sptq_spenq +#define spdeq sptq_spdeq +#define splay sptq_splay +#define sphead sptq_sphead +#define spdelete sptq_spdelete +#define splookup sptq_splookup +#define spscan sptq_spscan +#define spfhead sptq_spfhead +#define spfnext sptq_spfnext /* Original file: sptree.cpp * @@ -84,7 +42,6 @@ Hines changed to void spinit(SPTREE**) for use with TQueue. * int spempty(); Is tree empty? * SPBLK *spenq( n, q ) Insert n in q after all equal keys. * SPBLK *spdeq( np ) Return first key under *np, removing it. - * SPBLK *spenqprior( n, q ) Insert n in q before all equal keys. * void splay( n, q ) n (already in q) becomes the root. * * In the above, n points to an SPBLK type, while q points to an @@ -134,13 +91,8 @@ Hines changed to void spinit(SPTREE**) for use with TQueue. */ template void spinit(SPTREE* q) { - q->lookups = 0; - q->lkpcmps = 0; - q->enqs = 0; q->enqcmps = 0; - q->splays = 0; - q->splayloops = 0; - q->root = NULL; + q->root = nullptr; } /*---------------- @@ -148,8 +100,8 @@ void spinit(SPTREE* q) { * spempty() -- is an event-set represented as a splay tree empty? */ template -int spempty(SPTREE* q) { - return (q == NULL || q->root == NULL); +bool spempty(SPTREE* q) { + return (q == nullptr || q->root == nullptr); } @@ -175,14 +127,13 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { double key; int Sct; /* Strcmp value */ - q->enqs++; - n->uplink = NULL; + n->uplink = nullptr; next = q->root; q->root = n; - if (next == NULL) /* trivial enq */ + if (next == nullptr) /* trivial enq */ { - n->leftlink = NULL; - n->rightlink = NULL; + n->leftlink = nullptr; + n->rightlink = nullptr; } else /* difficult enq */ { key = n->key; @@ -202,10 +153,10 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { do /* walk to the right in the left tree */ { temp = next->rightlink; - if (temp == NULL) { + if (temp == nullptr) { left->rightlink = next; next->uplink = left; - right->leftlink = NULL; + right->leftlink = nullptr; goto done; /* job done, entire tree split */ } @@ -219,7 +170,7 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { } next->rightlink = temp->leftlink; - if (temp->leftlink != NULL) + if (temp->leftlink != nullptr) temp->leftlink->uplink = next; left->rightlink = temp; temp->uplink = left; @@ -227,8 +178,8 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { next->uplink = temp; left = temp; next = temp->rightlink; - if (next == NULL) { - right->leftlink = NULL; + if (next == nullptr) { + right->leftlink = nullptr; goto done; /* job done, entire tree split */ } @@ -241,10 +192,10 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { do /* walk to the left in the right tree */ { temp = next->leftlink; - if (temp == NULL) { + if (temp == nullptr) { right->leftlink = next; next->uplink = right; - left->rightlink = NULL; + left->rightlink = nullptr; goto done; /* job done, entire tree split */ } @@ -257,7 +208,7 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { goto one; /* change sides */ } next->leftlink = temp->rightlink; - if (temp->rightlink != NULL) + if (temp->rightlink != nullptr) temp->rightlink->uplink = next; right->leftlink = temp; temp->uplink = right; @@ -265,8 +216,8 @@ SPBLK* spenq(SPBLK* n, SPTREE* q) { next->uplink = temp; right = temp; next = temp->leftlink; - if (next == NULL) { - left->rightlink = NULL; + if (next == nullptr) { + left->rightlink = nullptr; goto done; /* job done, entire tree split */ } @@ -307,37 +258,37 @@ SPBLK* spdeq(SPBLK** np) /* pointer to a node pointer */ SPBLK* farleft; /* the left child of left */ SPBLK* farfarleft; /* the left child of farleft */ - if (np == NULL || *np == NULL) { - deq = NULL; + if (np == nullptr || *np == nullptr) { + deq = nullptr; } else { next = *np; left = next->leftlink; - if (left == NULL) { + if (left == nullptr) { deq = next; *np = next->rightlink; - if (*np != NULL) - (*np)->uplink = NULL; + if (*np != nullptr) + (*np)->uplink = nullptr; } else for (;;) /* left is not null */ { - /* next is not it, left is not NULL, might be it */ + /* next is not it, left is not nullptr, might be it */ farleft = left->leftlink; - if (farleft == NULL) { + if (farleft == nullptr) { deq = left; next->leftlink = left->rightlink; - if (left->rightlink != NULL) + if (left->rightlink != nullptr) left->rightlink->uplink = next; break; } - /* next, left are not it, farleft is not NULL, might be it */ + /* next, left are not it, farleft is not nullptr, might be it */ farfarleft = farleft->leftlink; - if (farfarleft == NULL) { + if (farfarleft == nullptr) { deq = farleft; left->leftlink = farleft->rightlink; - if (farleft->rightlink != NULL) + if (farleft->rightlink != nullptr) farleft->rightlink->uplink = left; break; } @@ -346,7 +297,7 @@ SPBLK* spdeq(SPBLK** np) /* pointer to a node pointer */ next->leftlink = farleft; farleft->uplink = next; left->leftlink = farleft->rightlink; - if (farleft->rightlink != NULL) + if (farleft->rightlink != nullptr) farleft->rightlink->uplink = left; farleft->rightlink = left; left->uplink = farleft; @@ -360,130 +311,6 @@ SPBLK* spdeq(SPBLK** np) /* pointer to a node pointer */ } /* spdeq */ -/*---------------- - * - * spenqprior() -- insert into tree before other equal keys. - * - * put n in q before all other nodes with the same key; after this is - * done, n will be the root of the splay tree representing q, all nodes in - * q with keys less than that of n will be in the left subtree, all with - * greater or equal keys will be in the right subtree; the tree is split - * into these subtrees from the top down, with rotations performed along - * the way to shorten the left branch of the right subtree and the right - * branch of the left subtree; the logic of spenqprior is exactly the - * same as that of spenq except for a substitution of comparison - * operators - */ -template -SPBLK* spenqprior(SPBLK* n, SPTREE* q) { - SPBLK* left; /* the rightmost node in the left tree */ - SPBLK* right; /* the leftmost node in the right tree */ - SPBLK* next; /* the root of unsplit part of tree */ - SPBLK* temp; - int Sct; /* Strcmp value */ - double key; - - n->uplink = NULL; - next = q->root; - q->root = n; - if (next == NULL) /* trivial enq */ - { - n->leftlink = NULL; - n->rightlink = NULL; - } else /* difficult enq */ - { - key = n->key; - left = n; - right = n; - - /* n's left and right children will hold the right and left - splayed trees resulting from splitting on n->key; - note that the children will be reversed! */ - - if (STRCMP(next->key, key) >= 0) - goto two; - - one: /* assert next->key < key */ - - do /* walk to the right in the left tree */ - { - temp = next->rightlink; - if (temp == NULL) { - left->rightlink = next; - next->uplink = left; - right->leftlink = NULL; - goto done; /* job done, entire tree split */ - } - if (STRCMP(temp->key, key) >= 0) { - left->rightlink = next; - next->uplink = left; - left = next; - next = temp; - goto two; /* change sides */ - } - next->rightlink = temp->leftlink; - if (temp->leftlink != NULL) - temp->leftlink->uplink = next; - left->rightlink = temp; - temp->uplink = left; - temp->leftlink = next; - next->uplink = temp; - left = temp; - next = temp->rightlink; - if (next == NULL) { - right->leftlink = NULL; - goto done; /* job done, entire tree split */ - } - - } while (STRCMP(next->key, key) < 0); /* change sides */ - - two: /* assert next->key >= key */ - - do /* walk to the left in the right tree */ - { - temp = next->leftlink; - if (temp == NULL) { - right->leftlink = next; - next->uplink = right; - left->rightlink = NULL; - goto done; /* job done, entire tree split */ - } - if (STRCMP(temp->key, key) < 0) { - right->leftlink = next; - next->uplink = right; - right = next; - next = temp; - goto one; /* change sides */ - } - next->leftlink = temp->rightlink; - if (temp->rightlink != NULL) - temp->rightlink->uplink = next; - right->leftlink = temp; - temp->uplink = right; - temp->rightlink = next; - next->uplink = temp; - right = temp; - next = temp->leftlink; - if (next == NULL) { - left->rightlink = NULL; - goto done; /* job done, entire tree split */ - } - - } while (STRCMP(next->key, key) >= 0); /* change sides */ - - goto one; - - done: /* split is done, branches of n need reversal */ - - temp = n->leftlink; - n->leftlink = n->rightlink; - n->rightlink = temp; - } - - return (n); - -} /* spenqprior */ - /*---------------- * * splay() -- reorganize the tree. @@ -496,7 +323,7 @@ SPBLK* spenqprior(SPBLK* n, SPTREE* q) { * the right subtree and the right branch of the left subtree are * shortened in the process * - * this code assumes that n is not NULL and is in q; it can sometimes + * this code assumes that n is not nullptr and is in q; it can sometimes * detect n not in q and complain */ @@ -515,26 +342,22 @@ void splay(SPBLK* n, SPTREE* q) { prev = n; up = prev->uplink; - q->splays++; - - while (up != NULL) { - q->splayloops++; - + while (up != nullptr) { /* walk up the tree towards the root, splaying all to the left of n into the left subtree, all to right into the right subtree */ upup = up->uplink; if (up->leftlink == prev) /* up is to the right of n */ { - if (upup != NULL && upup->leftlink == up) /* rotate */ + if (upup != nullptr && upup->leftlink == up) /* rotate */ { upupup = upup->uplink; upup->leftlink = up->rightlink; - if (upup->leftlink != NULL) + if (upup->leftlink != nullptr) upup->leftlink->uplink = upup; up->rightlink = upup; upup->uplink = up; - if (upupup == NULL) + if (upupup == nullptr) q->root = up; else if (upupup->leftlink == upup) upupup->leftlink = up; @@ -544,21 +367,21 @@ void splay(SPBLK* n, SPTREE* q) { upup = upupup; } up->leftlink = right; - if (right != NULL) + if (right != nullptr) right->uplink = up; right = up; } else /* up is to the left of n */ { - if (upup != NULL && upup->rightlink == up) /* rotate */ + if (upup != nullptr && upup->rightlink == up) /* rotate */ { upupup = upup->uplink; upup->rightlink = up->leftlink; - if (upup->rightlink != NULL) + if (upup->rightlink != nullptr) upup->rightlink->uplink = upup; up->leftlink = upup; upup->uplink = up; - if (upupup == NULL) + if (upupup == nullptr) q->root = up; else if (upupup->rightlink == upup) upupup->rightlink = up; @@ -568,7 +391,7 @@ void splay(SPBLK* n, SPTREE* q) { upup = upupup; } up->rightlink = left; - if (left != NULL) + if (left != nullptr) left->uplink = up; left = up; } @@ -585,12 +408,12 @@ void splay(SPBLK* n, SPTREE* q) { n->leftlink = left; n->rightlink = right; - if (left != NULL) + if (left != nullptr) left->uplink = n; - if (right != NULL) + if (right != nullptr) right->uplink = n; q->root = n; - n->uplink = NULL; + n->uplink = nullptr; } /* splay */ @@ -602,10 +425,6 @@ void splay(SPBLK* n, SPTREE* q) { n = sphead( q ) n is the head item in q (not removed). spdelete( n, q ) n is removed from q. - n = spnext( np, q ) n is the successor of np in q. - n = spprev( np, q ) n is the predecessor of np in q. - spenqbefore( n, np, q ) n becomes the predecessor of np in q. - spenqafter( n, np, q ) n becomes the successor of np in q. In the above, n and np are pointers to single items (type SPBLK *); q is an event-set (type SPTREE *), @@ -689,11 +508,11 @@ SPBLK* sphead(SPTREE* q) { /* splay version, good amortized bound */ x = spdeq(&q->root); - if (x != NULL) { + if (x != nullptr) { x->rightlink = q->root; - x->leftlink = NULL; - x->uplink = NULL; - if (q->root != NULL) + x->leftlink = nullptr; + x->uplink = nullptr; + if (q->root != nullptr) q->root->uplink = x; } q->root = x; @@ -701,12 +520,6 @@ SPBLK* sphead(SPTREE* q) { /* alternative version, bad amortized bound, but faster on the average */ -#if 0 - x = q->root; - while( x->leftlink != NULL ) - x = x->leftlink; -#endif - return (x); } /* sphead */ @@ -726,19 +539,19 @@ void spdelete(SPBLK* n, SPTREE* q) { splay(n, q); x = spdeq(&q->root->rightlink); - if (x == NULL) /* empty right subtree */ + if (x == nullptr) /* empty right subtree */ { q->root = q->root->leftlink; if (q->root) - q->root->uplink = NULL; + q->root->uplink = nullptr; } else /* non-empty right subtree */ { - x->uplink = NULL; + x->uplink = nullptr; x->leftlink = q->root->leftlink; x->rightlink = q->root->rightlink; - if (x->leftlink != NULL) + if (x->leftlink != nullptr) x->leftlink->uplink = x; - if (x->rightlink != NULL) + if (x->rightlink != nullptr) x->rightlink->uplink = x; q->root = x; } @@ -746,140 +559,6 @@ void spdelete(SPBLK* n, SPTREE* q) { } /* spdelete */ -/*---------------- - * - * spnext() -- return next higer item in the tree, or NULL. - * - * return the successor of n in q, represented as a splay tree; the - * successor becomes the root; two alternate versions are provided, - * one which is shorter, but slower, and one which is faster on the - * average because it does not do any splaying - * - */ -template -SPBLK* spnext(SPBLK* n, SPTREE* q) { - SPBLK* next; - SPBLK* x; - - /* splay version */ - splay(n, q); - x = spdeq(&n->rightlink); - if (x != NULL) { - x->leftlink = n; - n->uplink = x; - x->rightlink = n->rightlink; - n->rightlink = NULL; - if (x->rightlink != NULL) - x->rightlink->uplink = x; - q->root = x; - x->uplink = NULL; - } - next = x; - - /* shorter slower version; - deleting last "if" undoes the amortized bound */ - -#if 0 - splay( n, q ); - x = n->rightlink; - if( x != NULL ) - while( x->leftlink != NULL ) - x = x->leftlink; - next = x; - if( x != NULL ) - splay( x, q ); -#endif - - return (next); - -} /* spnext */ - - -/*---------------- - * - * spprev() -- return previous node in a tree, or NULL. - * - * return the predecessor of n in q, represented as a splay tree; - * the predecessor becomes the root; an alternate version is - * provided which is faster on the average because it does not do - * any splaying - * - */ -template -SPBLK* spprev(SPBLK* n, SPTREE* q) { - SPBLK* prev; - SPBLK* x; - - /* splay version; - note: deleting the last "if" undoes the amortized bound */ - - splay(n, q); - x = n->leftlink; - if (x != NULL) - while (x->rightlink != NULL) - x = x->rightlink; - prev = x; - if (x != NULL) - splay(x, q); - - return (prev); - -} /* spprev */ - - -/*---------------- - * - * spenqbefore() -- insert node before another in a tree. - * - * returns pointer to n. - * - * event n is entered in the splay tree q as the immediate - * predecessor of n1; in doing so, n1 becomes the root of the tree - * with n as its left son - * - */ -template -SPBLK* spenqbefore(SPBLK* n, SPBLK* n1, SPTREE* q) { - splay(n1, q); - n->key = n1->key; - n->leftlink = n1->leftlink; - if (n->leftlink != NULL) - n->leftlink->uplink = n; - n->rightlink = NULL; - n->uplink = n1; - n1->leftlink = n; - - return (n); - -} /* spenqbefore */ - - -/*---------------- - * - * spenqafter() -- enter n after n1 in tree q. - * - * returns a pointer to n. - * - * event n is entered in the splay tree q as the immediate - * successor of n1; in doing so, n1 becomes the root of the tree - * with n as its right son - */ -template -SPBLK* spenqafter(SPBLK* n, SPBLK* n1, SPTREE* q) { - splay(n1, q); - n->key = n1->key; - n->rightlink = n1->rightlink; - if (n->rightlink != NULL) - n->rightlink->uplink = n; - n->leftlink = NULL; - n->uplink = n1; - n1->rightlink = n; - - return (n); - -} /* spenqafter */ - - /* Original file: spdaveb.cpp * * spdaveb.cpp -- daveb's new splay tree functions. @@ -889,14 +568,9 @@ SPBLK* spenqafter(SPBLK* n, SPBLK* n1, SPTREE* q) { * replacement of one by the other. Hey, it worked for me! * * splookup() -- given a key, find a node in a tree. - * spinstall() -- install an item in the tree, overwriting existing value. * spfhead() -- fast (non-splay) find the first node in a tree. - * spftail() -- fast (non-splay) find the last node in a tree. * spscan() -- forward scan tree from the head. - * sprscan() -- reverse scan tree from the tail. * spfnext() -- non-splaying next. - * spfprev() -- non-splaying prev. - * spstats() -- make char string of stats for a tree. * * Written by David Brower, daveb@rtech.uucp 1/88. */ @@ -912,66 +586,22 @@ template SPBLK* splookup(double key, SPTREE* q) { SPBLK* n; int Sct; - int c; /* find node in the tree */ n = q->root; - c = ++(q->lkpcmps); - q->lookups++; // while( n && (Sct = STRCMP( key, n->key ) ) ) while (n && (Sct = key != n->key)) { - c++; n = (Sct < 0) ? n->leftlink : n->rightlink; } - q->lkpcmps = c; /* reorganize tree around this node */ - if (n != NULL) + if (n != nullptr) splay(n, q); return (n); } -#if 0 - -/*---------------- - * - * spinstall() -- install an entry in a tree, overwriting any existing node. - * - * If the node already exists, replace its contents. - * If it does not exist, then allocate a new node and fill it in. - */ - -SPBLK * -spinstall( key, data, datb, q ) - -char * key; -char * data; -char * datb; -SPTREE *q; - -{ - SPBLK *n; - - if( NULL == ( n = splookup( key, q ) ) ) - { - n = (SPBLK *) emalloc( sizeof( *n ) ); - n->key = key; - n->leftlink = NULL; - n->rightlink = NULL; - n->uplink = NULL; - spenq( n, q ); - } - - n->data = data; - n->datb = datb; - - return( n ); -} -#endif - - /*---------------- * * spfhead() -- return the "lowest" element in the tree. @@ -984,8 +614,8 @@ template SPBLK* spfhead(SPTREE* q) { SPBLK* x; - if (NULL != (x = q->root)) - while (x->leftlink != NULL) + if (nullptr != (x = q->root)) + while (x->leftlink != nullptr) x = x->leftlink; return (x); @@ -993,26 +623,6 @@ SPBLK* spfhead(SPTREE* q) { } /* spfhead */ -/*---------------- - * - * spftail() -- find the last node in a tree. - * - * Fast version does not splay result or intermediate steps. - */ -template -SPBLK* spftail(SPTREE* q) { - SPBLK* x; - - - if (NULL != (x = q->root)) - while (x->rightlink != NULL) - x = x->rightlink; - - return (x); - -} /* spftail */ - - /*---------------- * * spscan() -- apply a function to nodes in ascending order. @@ -1020,36 +630,17 @@ SPBLK* spftail(SPTREE* q) { * if n is given, start at that node, otherwise start from * the head. */ -class TQItem; - template void spscan(void (*f)(const SPBLK*, int), SPBLK* n, SPTREE* q) { SPBLK* x; - for (x = n != NULL ? n : spfhead(q); x != NULL; x = spfnext(x)) + for (x = n != nullptr ? n : spfhead(q); x != nullptr; x = spfnext(x)) (*f)(x, 0); } - /*---------------- * - * sprscan() -- apply a function to nodes in descending order. - * - * if n is given, start at that node, otherwise start from - * the tail. - */ -template -void sprscan(void (*f)(const TQItem*, int), SPBLK* n, SPTREE* q) { - SPBLK* x; - - for (x = n != NULL ? n : spftail(q); x != NULL; x = spfprev(x)) - (*f)(x, 0); -} - - -/*---------------- - * - * spfnext() -- fast return next higer item in the tree, or NULL. + * spfnext() -- fast return next higer item in the tree, or nullptr. * * return the successor of n in q, represented as a splay tree. * This is a fast (on average) version that does not splay. @@ -1063,22 +654,22 @@ SPBLK* spfnext(SPBLK* n) { * poor amortized bound */ - if (n == NULL) + if (n == nullptr) return (n); x = n->rightlink; - if (x != NULL) { - while (x->leftlink != NULL) + if (x != nullptr) { + while (x->leftlink != nullptr) x = x->leftlink; next = x; - } else /* x == NULL */ + } else /* x == nullptr */ { x = n->uplink; - next = NULL; - while (x != NULL) { + next = nullptr; + while (x != nullptr) { if (x->leftlink == n) { next = x; - x = NULL; + x = nullptr; } else { n = x; x = n->uplink; @@ -1089,76 +680,3 @@ SPBLK* spfnext(SPBLK* n) { return (next); } /* spfnext */ - - -/*---------------- - * - * spfprev() -- return fast previous node in a tree, or NULL. - * - * return the predecessor of n in q, represented as a splay tree. - * This is a fast (on average) version that does not splay. - */ -template -SPBLK* spfprev(SPBLK* n) { - SPBLK* prev; - SPBLK* x; - - /* a long version, - * avoids splaying for fast average, poor amortized bound - */ - - if (n == NULL) - return (n); - - x = n->leftlink; - if (x != NULL) { - while (x->rightlink != NULL) - x = x->rightlink; - prev = x; - } else { - x = n->uplink; - prev = NULL; - while (x != NULL) { - if (x->rightlink == n) { - prev = x; - x = NULL; - } else { - n = x; - x = n->uplink; - } - } - } - - return (prev); - -} /* spfprev */ - - -template -const char* spstats(SPTREE* q) { - static char buf[128]; - float llen; - float elen; - float sloops; - - if (q == NULL) - return (""); - - llen = q->lookups ? (float) q->lkpcmps / q->lookups : 0; - elen = q->enqs ? (float) q->enqcmps / q->enqs : 0; - sloops = q->splays ? (float) q->splayloops / q->splays : 0; - - Sprintf(buf, - "f(%d %4.2f) i(%d %4.2f) s(%d %4.2f)", - q->lookups, - llen, - q->enqs, - elen, - q->splays, - sloops); - - return (const char*) buf; -} - - -#endif diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 9197a8c700..1e8eaceed6 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -334,7 +334,7 @@ void TQueue::remove(TQItem* q) { STAT(nrem); if (q) { if (q == least_) { - if (sptree_->root) { + if (!spempty(sptree_)) { least_ = spdeq(&sptree_->root); } else { least_ = nullptr; @@ -355,7 +355,7 @@ TQItem* TQueue::atomic_dq(double tt) { if (least_ && least_->t_ <= tt) { q = least_; STAT(nrem); - if (sptree_->root) { + if (!spempty(sptree_)) { least_ = spdeq(&sptree_->root); } else { least_ = nullptr; From 3844d2e6b19e14133fa62702eff2387628e4a411 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Mon, 26 Feb 2024 20:23:57 +0100 Subject: [PATCH 18/26] Hoc binding of TQueue is never used (#2763) --- src/nrncvode/tqueue.cpp | 97 ----------------------------------------- src/nrniv/nrnclass.h | 6 +-- 2 files changed, 3 insertions(+), 100 deletions(-) diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 1e8eaceed6..352906f8d1 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -9,9 +9,6 @@ #include "tqueue.hpp" #include "pool.hpp" -#include "classreg.h" -#include "nrnoc2iv.h" - #define PROFILE 0 #include "profile.h" @@ -23,94 +20,6 @@ #define STAT(arg) /**/ #endif -static const char* errmess_; - -static double insert(void* v) { - TQueue* q = (TQueue*) v; - q->insert(*getarg(1), (void*) 1); - return 1.; -} -static double print(void* v) { - TQueue* q = (TQueue*) v; - q->print(); - return 1.; -} - -static double least(void* v) { - TQueue* q = (TQueue*) v; - TQItem* i = q->least(); - double x = -1e9; - if (i) { - x = i->t_; - } - return x; -} -static double rmleast(void* v) { - TQueue* q = (TQueue*) v; - TQItem* i = q->least(); - double x = -1e9; - if (i) { - x = i->t_; - q->remove(i); - } - return x; -} - -static double mvleast(void* v) { - TQueue* q = (TQueue*) v; - q->move_least(*getarg(1)); - return 1.; -} - -static double remove(void* v) { - TQueue* q = (TQueue*) v; - q->remove(q->find(*getarg(1))); - return 1.; -} - -static double find(void* v) { - TQueue* q = (TQueue*) v; - TQItem* i = q->find(*getarg(1)); - double x = -1e9; - if (i) { - x = i->t_; - q->remove(i); - } - return x; -} -static double stats(void* v) { - TQueue* q = (TQueue*) v; - q->statistics(); - return 1.; -} - -static Member_func members[] = {{"insrt", insert}, - {"least", least}, - {"move_least", mvleast}, - {"remove_least", rmleast}, - {"remove", remove}, - {"find", find}, - {"stats", stats}, - {"printf", print}, - {0, 0}}; - -static void* cons(Object*) { - assert(0); - TQueue* q = new TQueue(0); - return (void*) q; -} - -static void destruct(void* v) { - TQueue* q = (TQueue*) v; - delete q; -} - -void TQueue_reg() { - class2oc("TQueue", cons, destruct, members, NULL, NULL, NULL); -} - -//---------------- - // splay tree + bin queue limited to fixed step method // for event-sets or priority queues // this starts from the sptqueue.cpp file and adds a bin queue @@ -156,12 +65,6 @@ static void prnt(const TQItem* b, int level) { Printf("%g %c %d Q=%p D=%p\n", b->t_, b->data_ ? 'x' : 'o', b->cnt_, b, b->data_); } -static void chk(TQItem* b, int level) { - if (!b->check()) { - hoc_execerror("chk failed", errmess_); - } -} - TQueue::TQueue(TQItemPool* tp, int mkmut) { MUTCONSTRUCT(mkmut) tpool_ = tp; diff --git a/src/nrniv/nrnclass.h b/src/nrniv/nrnclass.h index 02659d7993..dcc42b1120 100644 --- a/src/nrniv/nrnclass.h +++ b/src/nrniv/nrnclass.h @@ -10,10 +10,10 @@ #endif #if USECVODE , - Cvode_reg(), TQueue_reg() + Cvode_reg() #endif #if USEDSP - , + , DSP_reg() #endif #if USEBBS @@ -33,7 +33,7 @@ #endif #if USECVODE , - Cvode_reg, TQueue_reg + Cvode_reg #endif #if USEDSP , From 27f0ae5c1941d56f4f25864d37f573729ae948ec Mon Sep 17 00:00:00 2001 From: rgourdine <88497408+rgourdine@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:50:34 -0500 Subject: [PATCH 19/26] add rxd plot to documentation (#2761) --- docs/python/images/rangevarplotrxd.png | Bin 0 -> 22291 bytes docs/python/visualization/rvarplt.rst | 32 ++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 docs/python/images/rangevarplotrxd.png diff --git a/docs/python/images/rangevarplotrxd.png b/docs/python/images/rangevarplotrxd.png new file mode 100644 index 0000000000000000000000000000000000000000..6928e368db6ccd635156bcb77a1a57799e2284db GIT binary patch literal 22291 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 S+(Q7UMVXmc7(Y3A>DF(#QHOZ| literal 0 HcmV?d00001 diff --git a/docs/python/visualization/rvarplt.rst b/docs/python/visualization/rvarplt.rst index 2bd850f8e4..9b95c7fb29 100755 --- a/docs/python/visualization/rvarplt.rst +++ b/docs/python/visualization/rvarplt.rst @@ -128,7 +128,37 @@ RangeVarPlot rvp... #specify range begin and end imp... #specify impedance computation g = h.Graph() - g.addobject(rvp) + g.addobject(rvp) + + Example (plotting a rxd species): + .. code-block:: + python + + from neuron import h + from neuron import rxd + import matplotlib.pyplot as plt + + dend1 = h.Section("dend1") + dend1.nseg =4 + + cyt1 = rxd.Region(dend1.wholetree(), nrn_region="i") + ca1 = rxd.Species(cyt1, name="ca1", charge=2, initial=1e-12) + + ca1.nodes(dend1(0.1))[0].include_flux(40) + ca1.nodes(dend1(0.4))[0].include_flux(-25) + ca1.nodes(dend1(0.7))[0].include_flux(70) + + h.finitialize(-65) + h.dt /= 512 + h.load_file("stdrun.hoc") + h.continuerun(0.025) + + a_1 = h.RangeVarPlot(ca1, dend1(0), dend1(1)) + a_1.plot(plt) + plt.show() + + .. image:: ../images/rangevarplotrxd.png + :align: center ---- From ed09605ab5e09bd06dfb3419876e19ec4bbf1450 Mon Sep 17 00:00:00 2001 From: Erik Heeren Date: Mon, 26 Feb 2024 21:53:24 +0100 Subject: [PATCH 20/26] A running job can be killed by a new push except on master (#2762) --- .github/workflows/coverage.yml | 5 +++-- .github/workflows/docs.yml | 3 ++- .github/workflows/external.yml | 3 ++- .github/workflows/formatting.yml | 3 ++- .github/workflows/neuron-ci.yml | 3 ++- .github/workflows/windows.yml | 3 ++- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d7c273564c..550c839355 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,7 +1,8 @@ name: NEURON Code Coverage concurrency: - group: ${{ github.workflow }}#${{ github.ref }} + # Don't cancel on master, creating a PR when a push workflow is already going will cancel the push workflow in favour of the PR workflow + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.run_id || github.event.number && github.head_ref || github.ref_name }} cancel-in-progress: true on: @@ -110,7 +111,7 @@ jobs: python -m pip install --upgrade pip -r nrn_requirements.txt python -m pip install --upgrade -r external/nmodl/requirements.txt python -m pip install --upgrade -r ci_requirements.txt - + - name: Build & Test id: build-test diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ffc24629e8..4fad29b18e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,7 +1,8 @@ name: NEURON Documentation concurrency: - group: ${{ github.workflow }}#${{ github.ref }} + # Don't cancel on master, creating a PR when a push workflow is already going will cancel the push workflow in favour of the PR workflow + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.run_id || github.event.number && github.head_ref || github.ref_name }} cancel-in-progress: true on: diff --git a/.github/workflows/external.yml b/.github/workflows/external.yml index 088168c161..ab06e55459 100644 --- a/.github/workflows/external.yml +++ b/.github/workflows/external.yml @@ -1,7 +1,8 @@ name: External CIs concurrency: - group: ${{ github.workflow }}#${{ github.ref }} + # Don't cancel on master, creating a PR when a push workflow is already going will cancel the push workflow in favour of the PR workflow + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.run_id || github.event.number && github.head_ref || github.ref_name }} cancel-in-progress: true on: diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index 8b6dbf62db..434bef743d 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -1,7 +1,8 @@ name: Check formatting concurrency: - group: ${{ github.workflow }}#${{ github.ref }} + # Don't cancel on master, creating a PR when a push workflow is already going will cancel the push workflow in favour of the PR workflow + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.run_id || github.event.number && github.head_ref || github.ref_name }} cancel-in-progress: true on: diff --git a/.github/workflows/neuron-ci.yml b/.github/workflows/neuron-ci.yml index 3c5d8ca9c1..b8610c80c6 100644 --- a/.github/workflows/neuron-ci.yml +++ b/.github/workflows/neuron-ci.yml @@ -1,7 +1,8 @@ name: NEURON CI concurrency: - group: ${{ github.workflow }}#${{ github.ref }} + # Don't cancel on master, creating a PR when a push workflow is already going will cancel the push workflow in favour of the PR workflow + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.run_id || github.event.number && github.head_ref || github.ref_name }} cancel-in-progress: true on: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 052a0d6679..499ba667b0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -1,7 +1,8 @@ name: Windows Installer concurrency: - group: ${{ github.workflow }}#${{ github.ref }} + # Don't cancel on master, creating a PR when a push workflow is already going will cancel the push workflow in favour of the PR workflow + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/master' && github.run_id || github.event.number && github.head_ref || github.ref_name }} cancel-in-progress: true on: From 082d447f3ec0f3d3cfc95aecd41cb2716daf8d6d Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Tue, 27 Feb 2024 10:28:51 +0100 Subject: [PATCH 21/26] Fix lookup in SPTree (#2759) --- src/nrncvode/sptree.hpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/nrncvode/sptree.hpp b/src/nrncvode/sptree.hpp index a46fa6327d..bc78cd3180 100644 --- a/src/nrncvode/sptree.hpp +++ b/src/nrncvode/sptree.hpp @@ -584,14 +584,9 @@ void spdelete(SPBLK* n, SPTREE* q) { */ template SPBLK* splookup(double key, SPTREE* q) { - SPBLK* n; - int Sct; - - /* find node in the tree */ - n = q->root; - // while( n && (Sct = STRCMP( key, n->key ) ) ) - while (n && (Sct = key != n->key)) { - n = (Sct < 0) ? n->leftlink : n->rightlink; + SPBLK* n = q->root; + while (n && key != n->key) { + n = key < n->key ? n->leftlink : n->rightlink; } /* reorganize tree around this node */ From 0bc1087e1a3ef996628e346dd1492a47ba113df4 Mon Sep 17 00:00:00 2001 From: Nicolas Cornu Date: Wed, 28 Feb 2024 19:08:53 +0700 Subject: [PATCH 22/26] Move TQItem to its own header (#2765) --- src/nrncvode/cvodeobj.h | 2 +- src/nrncvode/netcon.h | 2 +- src/nrncvode/tqitem.hpp | 13 +++++++++++++ src/nrncvode/tqueue.cpp | 14 -------------- src/nrncvode/tqueue.hpp | 19 ++----------------- 5 files changed, 17 insertions(+), 33 deletions(-) create mode 100644 src/nrncvode/tqitem.hpp diff --git a/src/nrncvode/cvodeobj.h b/src/nrncvode/cvodeobj.h index 19933ff057..54558af45d 100644 --- a/src/nrncvode/cvodeobj.h +++ b/src/nrncvode/cvodeobj.h @@ -7,10 +7,10 @@ #include "shared/nvector.h" #include "membfunc.h" #include "netcon.h" +#include "tqitem.hpp" class NetCvode; class Daspk; -class TQItem; class TQueue; typedef std::vector PreSynList; struct BAMech; diff --git a/src/nrncvode/netcon.h b/src/nrncvode/netcon.h index eed9eda2fd..107e25fd64 100644 --- a/src/nrncvode/netcon.h +++ b/src/nrncvode/netcon.h @@ -8,6 +8,7 @@ #include "nrnmpi.h" #include "nrnneosm.h" #include "pool.hpp" +#include "tqitem.hpp" #include @@ -25,7 +26,6 @@ class PreSyn; class PlayRecord; class Cvode; class TQueue; -class TQItem; struct NrnThread; class NetCvode; class HocEvent; diff --git a/src/nrncvode/tqitem.hpp b/src/nrncvode/tqitem.hpp new file mode 100644 index 0000000000..5730baf11a --- /dev/null +++ b/src/nrncvode/tqitem.hpp @@ -0,0 +1,13 @@ +#pragma once + +struct TQItem { + // This function is used by the pool to clean object, here this is useless. + void clear() const {}; + + void* data_{}; + double t_{}; + TQItem* left_{}; + TQItem* right_{}; + TQItem* parent_{}; + int cnt_{}; // reused: -1 means it is in the splay tree, >=0 gives bin +}; diff --git a/src/nrncvode/tqueue.cpp b/src/nrncvode/tqueue.cpp index 352906f8d1..8e51035838 100644 --- a/src/nrncvode/tqueue.cpp +++ b/src/nrncvode/tqueue.cpp @@ -43,20 +43,6 @@ of struct _spblk, we are really using TQItem void (*nrn_binq_enqueue_error_handler)(double, TQItem*); -TQItem::TQItem() { - left_ = 0; - right_ = 0; - parent_ = 0; -} - -TQItem::~TQItem() {} - -bool TQItem::check() { -#if DOCHECK -#endif - return true; -} - static void prnt(const TQItem* b, int level) { int i; for (i = 0; i < level; ++i) { diff --git a/src/nrncvode/tqueue.hpp b/src/nrncvode/tqueue.hpp index 0e5ce6202b..4ee6cf23cc 100644 --- a/src/nrncvode/tqueue.hpp +++ b/src/nrncvode/tqueue.hpp @@ -7,7 +7,8 @@ #include #include -class TQItem; +#include "tqitem.hpp" + using TQItemPool = MutexPool; // bin queue for the fixed step method for NetCons and PreSyns. Splay tree @@ -24,22 +25,6 @@ using TQItemPool = MutexPool; template struct SPTREE; -class TQItem { - public: - TQItem(); - virtual ~TQItem(); - bool check(); - void clear(){}; - - public: - void* data_; - double t_; - TQItem* left_; - TQItem* right_; - TQItem* parent_; - int cnt_; // reused: -1 means it is in the splay tree, >=0 gives bin -}; - // helper class for the TQueue (SplayTBinQueue). class BinQ { public: From 2a681764b6b8c1024dcea8d78d464c1c9e8968ce Mon Sep 17 00:00:00 2001 From: nrnhines Date: Thu, 29 Feb 2024 03:42:55 -0500 Subject: [PATCH 23/26] NrnProperty handles extracellular and no longer creates/wraps a Prop structure. (#2675) * Several places where use of vext causes could not translate legacy index 10 * Only remaining use of NrnProperty is MechanismStandard * std::vector* memb_func[type].parm_default default parameters. * Explicit c++ mechanism implementations use hoc_register_parm_default * NrnProperty does not construct Prop but uses std::vector params_ Default params come from memb_func[type].parm_defaults. NrnProperty only used by MechanismStandard NrnProperty API and implementation significantly modified. Needs tests * test MechanismStandard * MechanismStandard.is_array(varint) returns true if varint is an array. * doc update. NrnProperty.copy goes both directions. * kschan uses neuron::mechanism::detail::register_data_fields * KSChan must call register_data_fields if any variable names are changed. * MORPHOLOGY also needs a parm default vector. * avoid AddressSanitizer: stack-use-after-scope on address * make_pointprocess works (for the first time ever?) * 96% line coverage for hocmech.cpp * hocmech.cpp -Wall free * remove some unused functions * Almost complete coverage and consequent bug fixes. test_mechstd6 must be manually executed. * cover relevant symdir.cpp lines for this PR. --- .../python/modelspec/programmatic/hocmech.rst | 4 +- docs/python/programming/mechstan.rst | 17 + src/ivoc/symdir.cpp | 17 +- src/neuron/container/soa_container.hpp | 2 - src/nmodl/nocpout.cpp | 25 +- src/nrniv/bbsavestate.cpp | 48 +- src/nrniv/bbsavestate.h | 1 + src/nrniv/hocmech.cpp | 62 ++- src/nrniv/kschan.cpp | 226 ++++----- src/nrniv/kschan.h | 7 +- src/nrniv/ndatclas.cpp | 291 ++++++------ src/nrniv/ndatclas.h | 29 +- src/nrniv/nrnmenu.cpp | 49 +- src/nrniv/nrnmenu.h | 5 +- src/nrniv/savstate.cpp | 21 +- src/nrnoc/capac.cpp | 5 +- src/nrnoc/eion.cpp | 40 +- src/nrnoc/extcelln.cpp | 16 +- src/nrnoc/init.cpp | 25 + src/nrnoc/membfunc.h | 8 +- src/nrnoc/multicore.cpp | 24 +- src/nrnoc/nrn_ansi.h | 2 +- src/nrnoc/passive0.cpp | 7 +- src/nrnpython/nrnpy_nrn.cpp | 60 ++- src/oc/hoc_oop.cpp | 2 +- src/oc/oc_ansi.h | 2 +- test/CMakeLists.txt | 3 +- test/cover/unit_tests/cover.cpp | 41 ++ test/hoctests/tests/test_bbss.py | 39 ++ test/hoctests/tests/test_hocmech.py | 86 ++++ test/hoctests/tests/test_kschan.json | 438 +++++++++--------- test/hoctests/tests/test_kschan.py | 21 +- test/hoctests/tests/test_legacy_setpointer.py | 36 ++ test/hoctests/tests/test_mechstd.py | 180 +++++++ test/pytest_coreneuron/test_partrans.py | 5 +- 35 files changed, 1205 insertions(+), 639 deletions(-) create mode 100644 test/cover/unit_tests/cover.cpp create mode 100644 test/hoctests/tests/test_bbss.py create mode 100644 test/hoctests/tests/test_hocmech.py create mode 100644 test/hoctests/tests/test_legacy_setpointer.py create mode 100644 test/hoctests/tests/test_mechstd.py diff --git a/docs/python/modelspec/programmatic/hocmech.rst b/docs/python/modelspec/programmatic/hocmech.rst index be6162b15a..e6a6d69cdc 100755 --- a/docs/python/modelspec/programmatic/hocmech.rst +++ b/docs/python/modelspec/programmatic/hocmech.rst @@ -22,11 +22,11 @@ HOC-based Mechanisms Syntax: ``h.make_mechanism("suffix", "Template", "parm1 parm2 parm3 ...")`` - ``h.make_pointprocess("Name", "Template", "parm1 parm2 parm3 ...")`` + ``h.make_pointprocess("Template", "parm1 parm2 parm3 ...")`` Description: Installs the HOC (in particular, *not* Python) class called "Template" as a density membrane mechanism - called "suffix" or a POINT_PROCESS called Name. If the third argument exists it must be a space + called "suffix" or a POINT_PROCESS called "Template". If the last argument exists it must be a space separated list of public variables in the Template which are to be treated as PARAMETERs. Public variables not in this list are treated as ASSIGNED variables. The new mechanism is used in exactly the same diff --git a/docs/python/programming/mechstan.rst b/docs/python/programming/mechstan.rst index d6821fb052..084254140e 100755 --- a/docs/python/programming/mechstan.rst +++ b/docs/python/programming/mechstan.rst @@ -410,6 +410,23 @@ MechanismStandard (Parameter Control) ---- +.. method:: MechanismStandard.is_array + + + Syntax: + .. code-block:: + python + + bool = ms.is_array(index) + + + Description: + Returns True if the variable associated with the index is an array. + + +---- + + .. method:: MechanismStandard.name diff --git a/src/ivoc/symdir.cpp b/src/ivoc/symdir.cpp index 5fb09510a9..3a2de98cdf 100644 --- a/src/ivoc/symdir.cpp +++ b/src/ivoc/symdir.cpp @@ -51,7 +51,7 @@ class SymDirectoryImpl: public Observer { void load_object(); void load_aliases(); void load_template(); - void load_mechanism(Prop*, int, const char*); + void load_mechanism(const Prop*, int, const char*); void append(Symbol* sym, Objectdata* od, Object* o = NULL); void append(Object*); void un_append(Object*); @@ -496,20 +496,23 @@ void SymDirectoryImpl::load_section() { symbol_lists_.push_back(new SymbolItem(buf)); nrn_pushsec(sec); Node* nd = sec->pnode[i]; - for (Prop* p = nd->prop; p; p = p->next) { + for (const Prop* p = nd->prop; p; p = p->next) { load_mechanism(p, 0, xarg); } nrn_popsec(); } -void SymDirectoryImpl::load_mechanism(Prop* p, int type, const char* xarg) { - NrnProperty np(p); - if (np.is_point()) { +void SymDirectoryImpl::load_mechanism(const Prop* p, int vartype, const char* xarg) { + int type = p->_type; + if (memb_func[type].is_point) { return; } char buf[200]; - for (Symbol* sym = np.first_var(); np.more_var(); sym = np.next_var()) { - if (np.var_type(sym) == type || type == 0) { + Symbol* msym = memb_func[type].sym; + int cnt = msym->s_varn; + for (int i = 0; i < cnt; ++i) { + const Symbol* sym = msym->u.ppsym[i]; + if (nrn_vartype(sym) == vartype || vartype == 0) { if (ISARRAY(sym)) { int n = hoc_total_array_data(sym, 0); if (n > 5) { diff --git a/src/neuron/container/soa_container.hpp b/src/neuron/container/soa_container.hpp index 08591c9b32..7b0f990c4d 100644 --- a/src/neuron/container/soa_container.hpp +++ b/src/neuron/container/soa_container.hpp @@ -594,8 +594,6 @@ class AccumulateMemoryUsage { std::vector const& vec, int field_index, int array_dim) { - auto element_size = sizeof(detail::index_column_tag::type); - m_usage.stable_identifiers.size = vec.size() * (sizeof(vec[0]) + sizeof(size_t*)); m_usage.stable_identifiers.capacity = m_usage.stable_identifiers.size + (vec.capacity() - vec.size()) * sizeof(vec[0]); diff --git a/src/nmodl/nocpout.cpp b/src/nmodl/nocpout.cpp index 67a2d85672..48dd954a0e 100644 --- a/src/nmodl/nocpout.cpp +++ b/src/nmodl/nocpout.cpp @@ -103,6 +103,7 @@ extern int check_tables_threads(List*); List* syminorder; List* plotlist; List* defs_list; +Item* defs_list_parm_default; // where we insert initializer defaults int electrode_current = 0; int thread_data_index = 0; List* thread_cleanup_list; @@ -910,6 +911,7 @@ static const char *_mechanism[] = {\n\ q = q->next->next->next; } + defs_list_parm_default = lappendstr(defs_list, "\n"); Item* before_nrn_alloc = lappendstr(defs_list, "\n"); Lappendstr(defs_list, @@ -942,15 +944,29 @@ static const char *_mechanism[] = {\n\ parraycount); Lappendstr(defs_list, buf); Lappendstr(defs_list, " /*initialize range parameters*/\n"); + + // _parm_default allows implementation of a more robust NrnProperty + // that does not require a call to prop_alloc. + /* arrays have only a single default value of 0.0 */ + i = 0; + insertstr(defs_list_parm_default, "\n /* Used by NrnProperty */\n"); + insertstr(defs_list_parm_default, "static _nrn_mechanism_std_vector _parm_default{\n"); ITERATE(q, rangeparm) { s = SYM(q); if (s->subtype & ARRAY) { - continue; + d1 = 0.0; + } else { + decode_ustr(s, &d1, &d2, buf); + Sprintf(buf, " %s = _parm_default[%d]; /* %g */\n", s->name, i, d1); + Lappendstr(defs_list, buf); } - decode_ustr(s, &d1, &d2, buf); - Sprintf(buf, " %s = %g;\n", s->name, d1); - Lappendstr(defs_list, buf); + /* fill in the std::vector _parm_default initializer */ + Sprintf(buf, " %g, /* %s */\n", d1, s->name); + insertstr(defs_list_parm_default, buf); + ++i; } + insertstr(defs_list_parm_default, "};"); + if (point_process) { Lappendstr(defs_list, " }\n"); } @@ -1227,6 +1243,7 @@ extern void _cvode_abstol( Symbol**, double*, int);\n\n\ } } Lappendstr(defs_list, "_mechtype = nrn_get_mechtype(_mechanism[1]);\n"); + Lappendstr(defs_list, "hoc_register_parm_default(_mechtype, &_parm_default);\n"); if (!point_process) { Lappendstr(defs_list, " hoc_register_npy_direct(_mechtype, npy_direct_func_proc);\n"); diff --git a/src/nrniv/bbsavestate.cpp b/src/nrniv/bbsavestate.cpp index 79403af2dd..89b4554256 100644 --- a/src/nrniv/bbsavestate.cpp +++ b/src/nrniv/bbsavestate.cpp @@ -169,7 +169,6 @@ callback to bbss_early when needed. #include "bbsavestate.h" #include "classreg.h" -#include "ndatclas.h" #include "nrncvode.h" #include "nrnoc2iv.h" #include "nrnran123.h" @@ -1029,12 +1028,10 @@ static void ssi_def() { Symbol* s = hoc_lookup("NetCon"); nct = s->u.ctemplate; ssi = new StateStructInfo[n_memb_func]{}; - int sav = v_structure_change; for (int im = 0; im < n_memb_func; ++im) { if (!memb_func[im].sym) { continue; } - NrnProperty np{memb_func[im].sym->name}; // generally we only save STATE variables. However for // models containing a NET_RECEIVE block, we also need to // save everything except the parameters @@ -1045,16 +1042,19 @@ static void ssi_def() { // param array including PARAMETERs. if (pnt_receive[im]) { ssi[im].offset = 0; - ssi[im].size = np.prop()->param_size(); // sum over array dims + ssi[im].size = nrn_prop_param_size_[im]; // sum over array dims } else { - for (Symbol* sym = np.first_var(); np.more_var(); sym = np.next_var()) { - if (np.var_type(sym) == STATE || sym->subtype == _AMBIGUOUS) { + Symbol* msym = memb_func[im].sym; + for (int i = 0; i < msym->s_varn; ++i) { + Symbol* sym = msym->u.ppsym[i]; + int vartype = nrn_vartype(sym); + if (vartype == STATE || vartype == _AMBIGUOUS) { if (ssi[im].offset < 0) { - ssi[im].offset = np.prop_index(sym); + ssi[im].offset = sym->u.rng.index; } else { // assert what we assume: that after this code the variables we want are // `size` contiguous legacy indices starting at `offset` - assert(ssi[im].offset + ssi[im].size == np.prop_index(sym)); + assert(ssi[im].offset + ssi[im].size == sym->u.rng.index); } ssi[im].size += hoc_total_array_data(sym, 0); } @@ -1078,9 +1078,6 @@ static void ssi_def() { //} } } - // Following set to 1 when NrnProperty constructor calls prop_alloc. - // so set back to original value. - v_structure_change = sav; } // if we know the Point_process, we can find the NetCon @@ -1968,6 +1965,22 @@ void BBSaveState::seccontents(Section* sec) { } } +// If extracellular, then save/restore not just v but also vext +void BBSaveState::v_vext(Node* nd) { + if (nd->extnode) { + int n = 1 + nlayer; + std::vector tmp{}; + tmp.reserve(n); + tmp.push_back(static_cast(nd->v_handle())); + for (int i = 0; i < nlayer; ++i) { + tmp.push_back(&nd->extnode->v[i]); + } + f->d(n, tmp.data()); + } else { + f->d(1, nd->v_handle()); + } +} + // all Point_process and mechanisms -- except IGNORE point process instances void BBSaveState::node(Node* nd) { if (debug) { @@ -1976,7 +1989,7 @@ void BBSaveState::node(Node* nd) { } int i; Prop* p; - f->d(1, nd->v_handle()); + v_vext(nd); // count // On restore, new point processes may have been inserted in // the section and marked IGNORE. So we need to count only the @@ -2015,7 +2028,11 @@ void BBSaveState::node01(Section* sec, Node* nd) { // It is not clear why the zero area node voltages need to be saved. // Without them, we get correct simulations after a restore for // whole cells but not for split cells. - f->d(1, nd->v_handle()); + // I don't know if there is duplicate saving of zero area node voltages. + // or, if so, it can be avoided. There was mention above of split cells + // and, with extracellular, there can be no such thing. + v_vext(nd); + // count for (i = 0, p = nd->prop; p; p = p->next) { if (memb_func[p->_type].is_point) { @@ -2055,7 +2072,10 @@ void BBSaveState::mech(Prop* p) { char buf[100]; Sprintf(buf, "//%s", memb_func[type].sym->name); f->s(buf, 1); - { + + if (type == EXTRACELL) { + // skip - vext states saved by caller and we are not saving parameters. + } else { auto const size = ssi[p->_type].size; // sum over array dimensions for range variables auto& random_indices = nrn_mech_random_indices(p->_type); auto size_random = random_indices.size(); diff --git a/src/nrniv/bbsavestate.h b/src/nrniv/bbsavestate.h index 1b537f534a..3be6f7a9ec 100644 --- a/src/nrniv/bbsavestate.h +++ b/src/nrniv/bbsavestate.h @@ -49,6 +49,7 @@ class BBSaveState { void seccontents(Section*); void node(Node*); void node01(Section*, Node*); + void v_vext(Node*); void mech(Prop*); void netrecv_pp(Point_process*); void possible_presyn(int gid); diff --git a/src/nrniv/hocmech.cpp b/src/nrniv/hocmech.cpp index e72027c07e..3437b73ef9 100644 --- a/src/nrniv/hocmech.cpp +++ b/src/nrniv/hocmech.cpp @@ -13,6 +13,7 @@ extern Symlist* hoc_symlist; extern void hoc_unlink_symbol(Symbol*, Symlist*); extern void hoc_link_symbol(Symbol*, Symlist*); extern void nrn_loc_point_process(int, Point_process*, Section*, Node*); +extern cTemplate** nrn_pnt_template_; extern char* pnt_map; extern Symbol** pointsym; extern Prop* nrn_point_prop_; @@ -34,6 +35,7 @@ class HocMech { Symbol* initial; // INITIAL proc initial() Symbol* after_step; // SOLVE ... METHOD after_cvode;proc after_step() Symlist* slist; // point process range variables. + std::vector parm_default{}; }; static void check(const char* s) { @@ -48,6 +50,21 @@ static void check_list(const char* s, Symlist* sl) { } } +static void seg_or_x_arg_inside_stack(int i, Section** psec, double* px) { + extern void (*nrnpy_o2loc_p_)(Object*, Section**, double*); + if (hoc_inside_stacktype(i) == NUMBER) { + *px = hoc_look_inside_stack(i); + *psec = chk_access(); + } else { + Object* o = hoc_look_inside_stack(i); + *psec = nullptr; + if (nrnpy_o2loc_p_) { + (*nrnpy_o2loc_p_)(o, psec, px); + } + assert(*psec); + } +} + void hoc_construct_point(Object* ob, int narg) { if (skip_) { // printf("skipped hoc_construct_point\n"); @@ -61,15 +78,10 @@ void hoc_construct_point(Object* ob, int narg) { assert(last_created_pp_ob_ == NULL); last_created_pp_ob_ = ob; if (narg > 0) { - auto const x = hoc_look_inside_stack(narg - 1); - // printf("x=%g\n", x); - Section* sec = chk_access(); + Section* sec; + double x; + seg_or_x_arg_inside_stack(narg - 1, &sec, &x); Node* nd = node_exact(sec, x); - // printf("ptype=%d pnt=%p %s nd=%p\n", ptype, pnt, secname(sec), nd); - // printf("type=%d pointsym=%p\n", type, pointsym[ptype]); - // printf("type=%d from pointsym %s = %d\n", type, pointsym[ptype]->name, - // pointsym[ptype]->subtype); - nrn_loc_point_process(ptype, pnt, sec, nd); } } @@ -100,8 +112,9 @@ int special_pnt_call(Object* ob, Symbol* sym, int narg) { if (narg != 1) { hoc_execerror("no argument", 0); } - auto const x = hoc_look_inside_stack(narg - 1); - Section* sec = chk_access(); + Section* sec; + double x; + seg_or_x_arg_inside_stack(narg - 1, &sec, &x); Node* node = node_exact(sec, x); nrn_loc_point_process(ptype, ob2pntproc(ob), sec, node); hoc_pushx(x); @@ -134,7 +147,7 @@ static void alloc_pnt(Prop* p) { p->ob = nrn_point_prop_->ob; // printf("p->ob comes from nrn_point_prop_ %s\n", hoc_object_name(p->ob)); } else { - nrn_prop_datum_alloc(p->_type, 2, p); + p->dparam = nrn_prop_datum_alloc(p->_type, 2, p); if (last_created_pp_ob_) { p->ob = last_created_pp_ob_; // printf("p->ob comes from last_created %s\n", hoc_object_name(p->ob)); @@ -205,6 +218,10 @@ static HocMech* common_register(const char** m, } register_mech(m, hm_alloc, cur, jacob, stat, initialize, -1, 0); type = nrn_get_mechtype(m[1]); + + // parm_default currently empty. That is ok. But fill in if + // implementation provides a default method. + hoc_register_parm_default(type, &hm->parm_default); hoc_register_cvode(type, nullptr, nullptr, nullptr, nullptr); memb_func[type].hoc_mech = hm; return hm; @@ -226,7 +243,7 @@ void make_mechanism() { } // if(parnames) printf("parnames=%s\n", parnames); Symbol* classsym = hoc_lookup(classname); - if (classsym->type != TEMPLATE) { + if (!classsym || classsym->type != TEMPLATE) { hoc_execerror(classname, "not a template"); } cTemplate* tp = classsym->u.ctemplate; @@ -235,6 +252,11 @@ void make_mechanism() { common_register(m, classsym, slist, alloc_mech, i); + // no SoA data fields. (the reference to the Hoc Object is in prop->ob) + std::vector> params{}; + std::vector> dparams{}; + neuron::mechanism::detail::register_data_fields(i, params, dparams); + for (sp = slist->first; sp; sp = sp->next) { if (sp->type == VAR && sp->cpublic) { Sprintf(buf, "%s_%s", sp->name, m[1]); @@ -253,7 +275,6 @@ void make_mechanism() { } void make_pointprocess() { - char buf[256]; int i, cnt, type, ptype; Symbol *sp, *s2; char* classname = gargstr(1); @@ -265,7 +286,7 @@ void make_pointprocess() { } // if(parnames) printf("parnames=%s\n", parnames); Symbol* classsym = hoc_lookup(classname); - if (classsym->type != TEMPLATE) { + if (!classsym || classsym->type != TEMPLATE) { hoc_execerror(classname, "not a template"); } cTemplate* tp = classsym->u.ctemplate; @@ -294,11 +315,20 @@ void make_pointprocess() { Symlist* slsav = hoc_symlist; hoc_symlist = NULL; HocMech* hm = common_register(m, classsym, slist, alloc_pnt, type); + + // Only area and pntproc SoA dparam fields. (the reference to the Hoc Object is in prop->ob) + std::vector> params{}; + std::vector> dparams{}; + dparams.emplace_back("", "area"); + dparams.emplace_back("", "pntproc"); + neuron::mechanism::detail::register_data_fields(type, params, dparams); + hm->slist = hoc_symlist; hoc_symlist = slsav; s2 = hoc_table_lookup(m[1], hm->slist); assert(s2->subtype == type); // type = s2->subtype; + nrn_pnt_template_[type] = tp; ptype = point_reg_helper(s2); // printf("type=%d pointtype=%d %s %p\n", type, ptype, s2->name, s2); classsym->u.ctemplate->is_point_ = ptype; @@ -307,7 +337,7 @@ void make_pointprocess() { // move s2 out of HocMech->slist and into slist. // That is the one with the u.ppsym. // The only reason it needs to be in slist is to find the - // mechanims type. And it needs to be LAST in that list. + // mechanism type. And it needs to be LAST in that list. // The only reason for the u.ppsym is for ndatclas.cpp and we // need to fill those symbols with oboff. sp = hoc_table_lookup(classsym->name, slist); @@ -318,7 +348,7 @@ void make_pointprocess() { for (i = 0; i < s2->s_varn; ++i) { Symbol* sp = hoc_table_lookup(s2->u.ppsym[i]->name, slist); s2->u.ppsym[i]->cpublic = 2; - s2->u.ppsym[1]->u.oboff = sp->u.oboff; + s2->u.ppsym[i]->u.oboff = sp->u.oboff; } for (i = 0; i < cnt; ++i) { if (m[i]) { diff --git a/src/nrniv/kschan.cpp b/src/nrniv/kschan.cpp index b3ab59ddc1..3c6f21faf4 100644 --- a/src/nrniv/kschan.cpp +++ b/src/nrniv/kschan.cpp @@ -258,6 +258,7 @@ static double ks_gmax(void* v) { KSChan* ks = (KSChan*) v; if (ifarg(1)) { ks->gmax_deflt_ = chkarg(1, 0., 1e9); + ks->parm_default_fill(); } return ks->gmax_deflt_; } @@ -266,6 +267,7 @@ static double ks_erev(void* v) { KSChan* ks = (KSChan*) v; if (ifarg(1)) { ks->erev_deflt_ = chkarg(1, -1e9, 1e9); + ks->parm_default_fill(); } return ks->erev_deflt_; } @@ -807,6 +809,17 @@ void KSChan_reg() { KSSingle::idum_ = 0; } +void KSChan::parm_default_fill() { + parm_default.resize(0); + if (is_single()) { + parm_default.push_back(1.0); // NSingleIndex is first + } + parm_default.push_back(gmax_deflt_); + if (!ion_sym_) { + parm_default.push_back(erev_deflt_); + } +} + // param is gmax, g, i --- if change then change numbers below // state names are handled individually static const char* m_kschan_pat[] = {"0", "kschan", "gmax", 0, "g", "i", 0, 0, 0}; @@ -834,6 +847,11 @@ void KSChan::add_channel(const char** m) { hoc_built_in_symlist = hoc_symlist; hoc_symlist = sav; mechtype_ = nrn_get_mechtype(m[1]); + register_data_fields(); + + parm_default_fill(); + hoc_register_parm_default(mechtype_, &parm_default); + // printf("mechanism type is %d\n", mechtype_); hoc_register_cvode(mechtype_, ode_count, ode_map, ode_spec, ode_matsol); if (!channels) { @@ -843,7 +861,6 @@ void KSChan::add_channel(const char** m) { channels->push_back(nullptr); } channels->push_back(this); - neuron::model().add_mechanism(mechtype_, m[1]); // no floating point fields } KSChan::KSChan(Object* obj, bool is_p) { @@ -910,8 +927,7 @@ KSChan::KSChan(Object* obj, bool is_p) { m_kschan[7 + aoff] = 0; soffset_ = 3 + aoff; // first state points here in p array add_channel(m_kschan); - must_allow_size_update(is_single_, ion_sym_ != nullptr, nligand_, nstate_); - update_size(); + err_if_has_instances(); for (int i = 0; i < 9; ++i) if (m_kschan[i]) { free((void*) m_kschan[i]); @@ -929,14 +945,11 @@ KSChan::KSChan(Object* obj, bool is_p) { void KSChan::setname(const char* s) { // printf("KSChan::setname\n"); - int i; - if (strcmp(s, name_.c_str()) == 0) { - return; - } + err_if_has_instances(); name_ = s; if (mechsym_) { char old_suffix[100]; - i = 0; + int i = 0; while (strcmp(mechsym_->name, name_.c_str()) != 0 && looksym(name_.c_str())) { Printf("KSChan::setname %s already in use\n", name_.c_str()); Sprintf(old_suffix, "%s%d", s, i); @@ -956,7 +969,7 @@ void KSChan::setname(const char* s) { } Symbol* sp; - if (!is_point()) + if (!is_point()) { for (i = 0; i < rlsym_->s_varn; ++i) { sp = rlsym_->u.ppsym[i]; char* cp = strstr(sp->name, old_suffix); @@ -972,8 +985,10 @@ void KSChan::setname(const char* s) { sp->name = s1; } } + } // printf("%s renamed to %s\n", old_suffix+1, name_.c_str()); } + register_data_fields(); } void KSChan::power(KSGateComplex* gc, int p) { @@ -985,7 +1000,6 @@ void KSChan::power(KSGateComplex* gc, int p) { void KSChan::set_single(bool b, bool update) { if (!is_point()) { - b = false; return; } if (b && (ngate_ != 1 || gc_[0].power_ != 1 || nhhstate_ > 0 || nksstate_ < 2)) { @@ -996,15 +1010,16 @@ void KSChan::set_single(bool b, bool update) { 0); } // check before changing structure - must_allow_size_update(b, ion_sym_ != nullptr, nligand_, nstate_); + err_if_has_instances(); if (is_single()) { - memb_func[mechtype_].singchan_ = NULL; + memb_func[mechtype_].singchan_ = nullptr; delete_schan_node_data(); delete single_; - single_ = NULL; + single_ = nullptr; } is_single_ = b; + parm_default_fill(); if (update) { update_prop(); } @@ -1013,6 +1028,7 @@ void KSChan::set_single(bool b, bool update) { memb_func[mechtype_].singchan_ = singchan; alloc_schan_node_data(); } + register_data_fields(); } const char* KSChan::state(int i) { @@ -1020,8 +1036,7 @@ const char* KSChan::state(int i) { } int KSChan::trans_index(int s, int t) { - int i; - for (i = 0; i < ntrans_; ++i) { + for (int i = 0; i < ntrans_; ++i) { if (trans_[i].src_ == s && trans_[i].target_ == t) { return i; } @@ -1030,8 +1045,7 @@ int KSChan::trans_index(int s, int t) { } int KSChan::gate_index(int is) { - int i; - for (i = 1; i < ngate_; ++i) { + for (int i = 1; i < ngate_; ++i) { if (is < gc_[i].sindex_) { return i - 1; } @@ -1044,7 +1058,7 @@ void KSChan::update_prop() { // prop.dparam for density is [5ion], [2ligands] // prop.dparam for point is area, pnt, [singledata], [5ion], [2ligands] - // before doing anything destructive, see if update_size will succeed. + // before doing anything destructive, see if register will succeed. auto new_psize = 3; // prop->param: gmax, g, i auto new_dsize = 0; // prop->dparam: empty auto new_ppoff = 0; @@ -1069,9 +1083,8 @@ void KSChan::update_prop() { } new_dsize += 2 * nligand_; new_psize += nstate_; - must_allow_size_update(is_single_, ion_sym_ != nullptr, nligand_, nstate_); + err_if_has_instances(); - int i; Symbol* searchsym = (is_point() ? mechsym_ : NULL); // some old sym pointers @@ -1090,8 +1103,6 @@ void KSChan::update_prop() { soffset_ = new_soffset; gmaxoffset_ = new_gmaxoffset; - update_size(); - // range variable names associated with prop->param rlsym_->s_varn = psize_; // problem here Symbol** ppsym = newppsym(rlsym_->s_varn); @@ -1141,19 +1152,19 @@ void KSChan::update_prop() { rlsym_->u.ppsym = ppsym; setcond(); ion_consist(); + register_data_fields(); } void KSChan::setion(const char* s) { // printf("KSChan::setion\n"); - int i; if (strcmp(ion_.c_str(), s) == 0) { return; } - // before doing anything destructive, check if update_size is going to succeed below + // before doing anything destructive, check if register is going to succeed below std::string new_ion{strlen(s) == 0 ? "NonSpecific" : s}; - must_allow_size_update(is_single_, new_ion != "NonSpecific", nligand_, nstate_); + err_if_has_instances(); - // now we know update_size will succeed, we can start modifying member data + // now we know register will succeed, we can start modifying member data Symbol* searchsym = (is_point() ? mechsym_ : NULL); ion_ = new_ion; char buf[100]; @@ -1165,7 +1176,7 @@ void KSChan::setion(const char* s) { printf("switch from useion to non-specific\n"); rlsym_->s_varn += 1; Symbol** ppsym = newppsym(rlsym_->s_varn); - for (i = 0; i <= io; ++i) { + for (int i = 0; i <= io; ++i) { ppsym[i] = rlsym_->u.ppsym[i]; } ion_sym_ = NULL; @@ -1182,7 +1193,7 @@ void KSChan::setion(const char* s) { ppsym[1 + io]->u.rng.type = rlsym_->subtype; ppsym[1 + io]->cpublic = 1; ppsym[1 + io]->u.rng.index = 1 + io; - for (i = 2 + io; i < rlsym_->s_varn; ++i) { + for (int i = 2 + io; i < rlsym_->s_varn; ++i) { ppsym[i] = rlsym_->u.ppsym[i - 1]; ppsym[i]->u.rng.index += 1; } @@ -1214,11 +1225,11 @@ void KSChan::setion(const char* s) { ion_sym_ = sym; rlsym_->s_varn -= 1; Symbol** ppsym = newppsym(rlsym_->s_varn); - for (i = 0; i <= io; ++i) { + for (int i = 0; i <= io; ++i) { ppsym[i] = rlsym_->u.ppsym[i]; } freesym(rlsym_->u.ppsym[1 + io], searchsym); - for (i = 1 + io; i < rlsym_->s_varn; ++i) { + for (int i = 1 + io; i < rlsym_->s_varn; ++i) { ppsym[i] = rlsym_->u.ppsym[i + 1]; ppsym[i]->u.rng.index -= 1; } @@ -1229,15 +1240,17 @@ void KSChan::setion(const char* s) { ion_consist(); } } - update_size(); - for (i = iligtrans_; i < ntrans_; ++i) { + parm_default_fill(); + for (int i = iligtrans_; i < ntrans_; ++i) { trans_[i].lig2pd(pdoff); } + register_data_fields(); } void KSChan::setsname(int i, const char* s) { state_[i].name_ = s; sname_install(); + register_data_fields(); } void KSChan::free1() { @@ -1332,6 +1345,7 @@ void KSChan::setcond() { if (is_point()) { ((KSPPIv*) iv_relation_)->ppoff_ = ppoff_; } + register_data_fields(); } void KSChan::settype(KSTransition* t, int type, const char* lig) { @@ -1511,7 +1525,7 @@ hoc_object_name(trans_[i].obj_)); KSState* KSChan::add_hhstate(const char* name) { int i; - must_allow_size_update(false, ion_sym_ != nullptr, nligand_, nstate_ + 1); + err_if_has_instances(); usetable(false); // new state, transition, gate, and f int is = nhhstate_; @@ -1532,12 +1546,13 @@ KSState* KSChan::add_hhstate(const char* name) { set_single(false); check_struct(); sname_install(); + register_data_fields(); setupmat(); return state_ + is; } KSState* KSChan::add_ksstate(int ig, const char* name) { - must_allow_size_update(false, ion_sym_ != nullptr, nligand_, nstate_ + 1); + err_if_has_instances(); // states must be added so that the gate states are in sequence int i, is; usetable(false); @@ -1569,13 +1584,14 @@ KSState* KSChan::add_ksstate(int ig, const char* name) { check_struct(); sname_install(); set_single(false); + register_data_fields(); setupmat(); return state_ + is; } void KSChan::remove_state(int is) { int i; - must_allow_size_update(false, ion_sym_ != nullptr, nligand_, nstate_ - 1); + err_if_has_instances(); usetable(false); if (is < nhhstate_) { state_remove(is); @@ -1633,12 +1649,13 @@ void KSChan::remove_state(int is) { set_single(false); check_struct(); sname_install(); + register_data_fields(); setupmat(); } KSTransition* KSChan::add_transition(int src, int target) { // does not deal here with ligands - must_allow_size_update(false, ion_sym_ != nullptr, nligand_, nstate_); + err_if_has_instances(); usetable(false); int it = iligtrans_; trans_insert(it, src, target); @@ -1652,7 +1669,7 @@ KSTransition* KSChan::add_transition(int src, int target) { void KSChan::remove_transition(int it) { // might reduce nstate, might reduce nligand. - must_allow_size_update(false, ion_sym_ != nullptr, nligand_, nstate_ - 1); + err_if_has_instances(); usetable(false); assert(it >= ivkstrans_); set_single(false); @@ -1714,9 +1731,9 @@ void KSChan::check_struct() { } KSState* KSChan::state_insert(int i, const char* n, double d) { - // before mutating any state, check if the call to update_size below will succeed + // before mutating any state, check if the call to register below will succeed auto const new_nstate = nstate_ + 1; - must_allow_size_update(is_single_, ion_sym_ != nullptr, nligand_, new_nstate); + err_if_has_instances(); int j; usetable(false); if (nstate_ >= state_size_) { @@ -1743,20 +1760,20 @@ KSState* KSChan::state_insert(int i, const char* n, double d) { } ++nstate_; assert(new_nstate == nstate_); - update_size(); for (j = 0; j < nstate_; ++j) { state_[j].index_ = j; if (state_[j].obj_) { state_[j].obj_->u.this_pointer = state_ + j; } } + register_data_fields(); return state_ + i; } void KSChan::state_remove(int i) { - // before mutating any state, check if the call to update_size below will succeed + // before mutating any state, check if the call to register below will succeed auto const new_nstate = nstate_ - 1; - must_allow_size_update(is_single_, ion_sym_ != nullptr, nligand_, new_nstate); + err_if_has_instances(); int j; usetable(false); unref(state_[i].obj_); @@ -1773,7 +1790,6 @@ void KSChan::state_remove(int i) { } --nstate_; assert(new_nstate == nstate_); - update_size(); state_[nstate_].obj_ = NULL; for (j = 0; j < nstate_; ++j) { state_[j].index_ = j; @@ -1781,6 +1797,7 @@ void KSChan::state_remove(int i) { state_[j].obj_->u.this_pointer = state_ + j; } } + register_data_fields(); } KSGateComplex* KSChan::gate_insert(int ig, int is, int power) { @@ -1897,10 +1914,9 @@ void KSChan::trans_remove(int i) { } void KSChan::setstructure(Vect* vec) { - // before mutating any state, check if the call to update_size below will succeed + // before mutating any state, check if the call to register below will succeed int const new_nstate = vec->elem(2); - int const new_nligand = vec->elem(5); - must_allow_size_update(is_single_, ion_sym_ != nullptr, new_nligand, new_nstate); + err_if_has_instances(); int i, j, ii, idx, ns; usetable(false); int nstate_old = nstate_; @@ -1914,7 +1930,6 @@ void KSChan::setstructure(Vect* vec) { ngate_ = (int) vec->elem(j++); nstate_ = (int) vec->elem(j++); assert(new_nstate == nstate_); - update_size(); nhhstate_ = (int) vec->elem(j++); nksstate_ = nstate_ - nhhstate_; ivkstrans_ = nhhstate_; @@ -1989,6 +2004,7 @@ void KSChan::setstructure(Vect* vec) { sname_install(); setupmat(); } + register_data_fields(); } void KSChan::sname_install() { @@ -2046,6 +2062,7 @@ void KSChan::sname_install() { state_[i].name_ = buf1; } } + register_data_fields(); } // check at the top and built-in level, or, if top!= NULL check the template @@ -2220,71 +2237,70 @@ void KSChan::mulmat(Memb_list* ml, } /** - * @brief Error if instances exist and number of variables will change. - * - * This is intended to allow methods that change the number of variables - * to check if success is possible before they make structure changes prior - * to calling update_size. - * Size changes are disallowed if any of following changes while - * instances exist: is_single, ion_sym_ NULL or not, nligand_, nstate_. + * @brief Error if instances exist */ -void KSChan::must_allow_size_update(bool single, bool ion, int nligand, int nstate) const { +void KSChan::err_if_has_instances() const { auto& mech_data = neuron::model().mechanism_data(mechtype_); - if (mech_data.empty()) { // size changes allowed since no existing instances + if (mech_data.empty()) { // (re)-registration allowed since no existing instances return; } - std::string s{""}; - if (single != is_single_) { - s = s + "single channel will change: "; - } - if (ion != (ion_sym_ != nullptr)) { - s = s + "switched beween ion and nonspecific: "; + std::string s = "KSChan:: Cannot change the names or number of mechanism variables while " + + std::to_string(mech_data.size()) + " instances are active"; + throw std::runtime_error(s); +} + +void KSChan::register_data_fields() { // or re-register + // prop.param is [Nsingle], gmax, [e], g, i, states + // prop.dparam for density is [5ion], [2ligands] + // prop.dparam for point is area, pnt, [singledata], [5ion], [2ligands] + + std::vector> params{}; + if (is_single()) { + params.emplace_back("Nsingle", 1); } - if (nligand != nligand_) { - s = s + "number of ligands will change: "; + params.emplace_back("gmax", 1); + if (ion_sym_ == nullptr) { + params.emplace_back("e", 1); } - if (nstate != nstate_) { - s = s + "number of states will change: "; + params.emplace_back("g", 1); + params.emplace_back("i", 1); + for (int i = 0; i < nstate_; ++i) { + params.emplace_back(state(i), 1); } - if (s == "") { - return; + // Dstate + std::string d{"D"}; + for (int i = 0; i < nstate_; ++i) { + params.emplace_back(d + state(i), 1); } - throw std::runtime_error( - "KSChan:: " + s + "Cannot change the number of mechanism variables while " + - std::to_string(neuron::model().mechanism_data(mechtype_).size()) + " instances are active"); -} -/** @brief Propagate changes to the number of param and dparam. - */ -void KSChan::update_size() { - auto& mech_data = neuron::model().mechanism_data(mechtype_); - std::size_t const new_param_size = soffset_ + 2 * nstate_; - std::size_t const new_dparam_size = (is_point_ ? 2 : 0) + (is_single_ ? 1 : 0) + - (ion_sym_ != nullptr ? 5 : 0) + 2 * nligand_; - auto const old_param_size = - mech_data.get_tag().num_variables(); - auto const old_dparam_size = nrn_mechanism_prop_datum_count(mechtype_); - if (new_param_size == old_param_size && new_dparam_size == old_dparam_size) { - // no change - return; + // Use strings instead of string.c_str() to keep alive and avoid + // AddressSanitizer: stack-use-after-scope on address + // Storing string.c_str() in param and dparam does not suffice. + + std::vector> dparams{}; + if (is_point()) { + dparams.emplace_back("_nd_area", "area"); + dparams.emplace_back("_pntproc", "pntproc"); + } + if (is_single()) { + dparams.emplace_back("singleptr", "pointer"); + } + if (ion_sym_) { + std::string name{ion_sym_->name}; + // the names don't matter + dparams.emplace_back(name + "_erev", name); + dparams.emplace_back(name + "_icur", name); + dparams.emplace_back(name + "_didv", name); + dparams.emplace_back(name + "_ci", name); + dparams.emplace_back(name + "_co", name); + } + for (int i = 0; i < nligand_; ++i) { + std::string name{ligands_[i]->name}; + dparams.emplace_back(name + "_ligo", name); + dparams.emplace_back(name + "_ligi", name); } - if (mech_data.size() > 0) { - // Should not happen since must_allow_size_update should have been - // called earlier. - throw std::runtime_error( - "KSChan::update_size() internal error: cannot change the number " - "of floating point fields from " + - std::to_string(old_param_size) + " to " + std::to_string(new_param_size) + - " or the number of Datum items from " + std::to_string(old_dparam_size) + " to " + - std::to_string(new_dparam_size) + " while " + std::to_string(mech_data.size()) + - " instances are active"); - } - std::vector new_param_info; - new_param_info.resize(new_param_size, {"kschan_field", 1}); - std::string mech_name{mech_data.name()}; - neuron::model().delete_mechanism(mechtype_); nrn_delete_mechanism_prop_datum(mechtype_); - neuron::model().add_mechanism(mechtype_, std::move(mech_name), std::move(new_param_info)); + neuron::mechanism::detail::register_data_fields(mechtype_, params, dparams); } void KSChan::alloc(Prop* prop) { @@ -2298,12 +2314,12 @@ void KSChan::alloc(Prop* prop) { // prop->param = nrn_point_prop_->param; prop->dparam = nrn_point_prop_->dparam; } else { - prop->param(gmaxoffset_) = gmax_deflt_; + prop->param(gmaxoffset_) = parm_default[gmaxoffset_]; // gmax_deflt_ if (is_point()) { - prop->param(NSingleIndex) = 1.; + prop->param(NSingleIndex) = parm_default[NSingleIndex]; // 1. } if (!ion_sym_) { - prop->param(1 + gmaxoffset_) = erev_deflt_; + prop->param(1 + gmaxoffset_) = parm_default[1 + gmaxoffset_]; // erev_deflt_; } } int ppsize = ppoff_; @@ -2388,7 +2404,7 @@ Prop* KSChan::needion(Symbol* s, Node* nd, Prop* pm) { * to the above three indicators and dparam size has not changed. However, * even though ion_sym != NULL is the same, that does not mean that the * specific ion used has not changed. And similarly for nligand. - * Thus, unless the must_allow_size_update is exended to include a change + * Thus, unless the must_allow_size_update is extended to include a change * in ions used, ion_consist must continue to update the ion usage. But it no * longer needs to realloc p->dparam. */ diff --git a/src/nrniv/kschan.h b/src/nrniv/kschan.h index de49789be5..b3fd32a26d 100644 --- a/src/nrniv/kschan.h +++ b/src/nrniv/kschan.h @@ -419,8 +419,8 @@ class KSChan { void delete_schan_node_data(); void alloc_schan_node_data(); void update_prop(); // can add and remove Nsingle and SingleNodeData - void update_size(); - void must_allow_size_update(bool single, bool ion, int ligand, int nstate) const; + void err_if_has_instances() const; + void register_data_fields(); KSState* state_insert(int i, const char* name, double frac); void state_remove(int i); @@ -443,6 +443,7 @@ class KSChan { std::string ion_; // name of ion , "" means non-specific double gmax_deflt_; double erev_deflt_; + void parm_default_fill(); int cond_model_; KSIv* iv_relation_; int ngate_; // number of gating complexes @@ -482,6 +483,8 @@ class KSChan { double vmin_, vmax_, dvinv_, dtsav_; int hh_tab_size_; bool usetable_; + + std::vector parm_default{}; }; #endif diff --git a/src/nrniv/ndatclas.cpp b/src/nrniv/ndatclas.cpp index d336e459ab..2d74ace692 100644 --- a/src/nrniv/ndatclas.cpp +++ b/src/nrniv/ndatclas.cpp @@ -11,28 +11,71 @@ extern void single_prop_free(Prop*); extern Symlist* hoc_built_in_symlist; extern Symlist* hoc_top_level_symlist; +/* +NrnProperty wrapping a Prop of type EXTRACELL has a problem with vext state +variables because those are not in the Prop.param array but in Node.ExtNode. +At the moment, knowing Prop* does not help with Node*. Note that +Node.ExtNode is not allocated by extcell_alloc(Prop*) but by +extcell_2d_alloc(Section*) when extracellular is inserted. +So, if NrnProperty is wrapping a "real" Prop, it is difficult to find +the Node to determine a pointer to vext[i]. +And if NrnProperty is just wrapping a NrnProperty allocated Prop, +the vext states are not allocated. In most cases the issue was avoided +because the "user" of NrnProperty knew the Node* + +It is attractive to get rid of NrnProperty altogether. It is mostly used +for iterating over mechanism variables and there are already fairly direct +methods to do that. However, NrnProperty, since it calls the mechanism +allocation method, provides default PARAMETER values. Apart from the +issue of default PARAMETER values, NrnProperty could just wrap a +std::vector param with a length that included PARAMETER, ASSIGNED, +and STATE (all the other defaults are 0.0). The advantage over +allocating a Prop is that the latter includes the extraneous +dparam as well as the full SoA and DataHandle apparatus. We could thread +the needle by allocating a Prop, copying the default PARAMETERS to the +std::vector, and destroying the Prop. Alternatively, the temporary Prop +allocation could be avoided by providing an additional mechanism method, e.g. +mech_param_defaults(mech_type, double* param) fill values starting at param +and avoid the side effect of SoA allocation (which increments +v_structure_change). + +*/ + //---------------------------------------------------- /* static */ class NrnPropertyImpl { friend class NrnProperty; - NrnPropertyImpl(Prop*); - ~NrnPropertyImpl(); + explicit NrnPropertyImpl(int mechtype); - Prop* p_; int iterator_; + int type_; Symbol* sym_; - bool del_; + std::vector params_{}; }; -NrnPropertyImpl::NrnPropertyImpl(Prop* p) { - p_ = p; - iterator_ = -1; - sym_ = memb_func[p_->_type].sym; - del_ = false; -} -NrnPropertyImpl::~NrnPropertyImpl() { - if (del_ && p_) { - // printf("NrnProperty freed prop\n"); - single_prop_free(p_); +NrnPropertyImpl::NrnPropertyImpl(int mechtype) + : iterator_(-1) + , type_(mechtype) + , sym_(memb_func[mechtype].sym) { + // How many values. nrn_prop_param_size[EXTRACELL] is not all of them + // Nor if it is a HocMech. + // And nrn_prop_param_size does not include array sizes. + int ntotal{}; + for (int i = 0; i < sym_->s_varn; ++i) { + Symbol* s = sym_->u.ppsym[i]; + ntotal += hoc_total_array_data(s, nullptr); + } + + params_.resize(ntotal); + auto& param_default = *memb_func[mechtype].parm_default; + int ix = 0; + int i = 0; + for (auto val: param_default) { + Symbol* s = sym_->u.ppsym[i]; + int n = hoc_total_array_data(s, nullptr); + for (int k = 0; k < n; ++k) { + params_[ix++] = val; + } + ++i; } } @@ -45,26 +88,6 @@ NrnPropertyImpl::~NrnPropertyImpl() { }; //---------------------------------------------------- -#if 0 -/* static */ class NrnSectionImpl { - friend NrnSection - NrnSectionImpl(Section*); - ~NrnSectionImpl(); - - Section* sec_; -}; -NrnSectionImpl::NrnSectionImpl(Section* sec) { - sec_ = sec; - section_ref(sec); -} -NrnSectionImpl::~NrnSectionImpl() { - section_unref(sec_); -} -#endif -//---------------------------------------------------- -NrnProperty::NrnProperty(Prop* p) { - npi_ = new NrnPropertyImpl(p); -} NrnProperty::NrnProperty(const char* name) { Symbol* sym = hoc_table_lookup(name, hoc_built_in_symlist); @@ -82,18 +105,7 @@ NrnProperty::NrnProperty(const char* name) { } } if (sym) { - Prop *p, *p0 = 0, *p1; - // printf("prop_alloc %s %p type=%d\n", sym->name, sym, sym->subtype); - // need to do this with no args - hoc_push_frame(sym, 0); - p = prop_alloc(&p0, sym->subtype, NULL); - hoc_pop_frame(); - for (; p0 != p; p0 = p1) { - p1 = p0->next; - single_prop_free(p0); - } - npi_ = new NrnPropertyImpl(p); - npi_->del_ = true; + npi_ = new NrnPropertyImpl(sym->subtype); } else { npi_ = NULL; hoc_execerror(name, "is not a Mechanism or Point Process"); @@ -101,27 +113,17 @@ NrnProperty::NrnProperty(const char* name) { } NrnProperty::~NrnProperty() { - delete npi_; -} - -bool NrnProperty::deleteable() { - return npi_->del_; + if (npi_) { + delete npi_; + } } const char* NrnProperty::name() const { return npi_->sym_->name; } -bool NrnProperty::is_point() const { - return memb_func[npi_->p_->_type].is_point; -} - int NrnProperty::type() const { - return npi_->p_->_type; -} - -Prop* NrnProperty::prop() const { - return npi_->p_; + return npi_->type_; } Symbol* NrnProperty::first_var() { @@ -150,91 +152,107 @@ Symbol* NrnProperty::var(int i) { return npi_->sym_->u.ppsym[i]; } -int NrnProperty::var_type(Symbol* sym) const { - return nrn_vartype(sym); +static std::string strip_suffix(const char* name, const char* suffix) { + std::string s = name; + std::string suf{"_"}; + suf += suffix; + s.resize(s.rfind(suf)); + return s; } -bool NrnProperty::assign(Prop* src, Prop* dest, int vartype) { +bool NrnProperty::copy(bool to_prop, Prop* dest, Node* nd_dest, int vartype) { assert(vartype != NRNPOINTER); - if (src && dest && src != dest && src->_type == dest->_type) { - if (src->ob) { - Symbol* msym = memb_func[src->_type].sym; - auto const cnt = msym->s_varn; - for (int i = 0; i < cnt; ++i) { - Symbol* sym = msym->u.ppsym[i]; - if (vartype == 0 || nrn_vartype(sym) == vartype) { - auto const jmax = hoc_total_array_data(sym, 0); - auto const n = sym->u.rng.index; - auto* const y = dest->ob->u.dataspace[n].pval; - auto* const x = src->ob->u.dataspace[n].pval; - for (int j = 0; j < jmax; ++j) { - y[j] = x[j]; + auto& x = npi_->params_; + Prop* p = dest; + if (!p || npi_->type_ != p->_type) { + return false; + } + + if (p->ob) { + Symbol* msym = memb_func[p->_type].sym; + auto const cnt = msym->s_varn; + // u.ppsym below are the right names but not the right symbols. + // Those symbols are in the p->ob->ctemplate->symtable + Symlist* symtab = p->ob->ctemplate->symtable; + int k = 0; + for (int i = 0; i < cnt; ++i) { + const Symbol* sym = msym->u.ppsym[i]; + auto const jmax = hoc_total_array_data(sym, 0); + if (vartype == 0 || nrn_vartype(sym) == vartype) { + const std::string& s = strip_suffix(sym->name, msym->name); + sym = hoc_table_lookup(s.c_str(), symtab); + assert(sym); + auto const n = sym->u.rng.index; + auto const y = p->ob->u.dataspace[n].pval; + for (int j = 0; j < jmax; ++j) { + if (to_prop) { + y[j] = x[k + j]; + } else { + x[k + j] = y[j]; } } } - } else { - if (vartype == 0) { - assert(dest->param_num_vars() == src->param_num_vars()); - auto const n = src->param_num_vars(); - for (int i = 0; i < n; ++i) { - assert(dest->param_array_dimension(i) == src->param_array_dimension(i)); - for (auto j = 0; j < src->param_array_dimension(i); ++j) { - dest->param(i, j) = src->param(i, j); - } - } - } else { - Symbol* msym = memb_func[src->_type].sym; - auto const cnt = msym->s_varn; - for (int i = 0; i < cnt; ++i) { - Symbol* sym = msym->u.ppsym[i]; - if (nrn_vartype(sym) == vartype) { - auto const jmax = hoc_total_array_data(sym, 0); - auto const n = sym->u.rng.index; - assert(src->param_size() == dest->param_size()); - for (int j = 0; j < jmax; ++j) { - dest->param_legacy(n + j) = src->param_legacy(n + j); + k += jmax; + } + } else { + Symbol* msym = memb_func[p->_type].sym; + auto const cnt = msym->s_varn; + int k = 0; + for (int i = 0; i < cnt; ++i) { + const Symbol* sym = msym->u.ppsym[i]; + auto const jmax = hoc_total_array_data(sym, 0); + if (vartype == 0 || nrn_vartype(sym) == vartype) { + auto const n = sym->u.rng.index; + for (int j = 0; j < jmax; ++j) { + if (p->_type == EXTRACELL && n == neuron::extracellular::vext_pseudoindex()) { + if (to_prop) { + nd_dest->extnode->v[j] = x[k + j]; + } else { + x[k + j] = nd_dest->extnode->v[j]; + } + } else { + if (to_prop) { + p->param_legacy(n + j) = x[k + j]; + } else { + x[k + j] = p->param_legacy(n + j); } } } } + k += jmax; } - return true; - } else { - return false; } + return true; +} + +bool NrnProperty::copy_out(NrnProperty& np, int /* vartype */) { + const auto& psrc = npi_->params_; + auto& pdest = np.npi_->params_; + pdest = psrc; + return true; } -Symbol* NrnProperty::find(const char* name) { - Symbol* sym; + +Symbol* NrnProperty::findsym(const char* name) { + Symbol* rsym = nullptr; int i, cnt; cnt = npi_->sym_->s_varn; for (i = 0; i < cnt; ++i) { - sym = npi_->sym_->u.ppsym[i]; + Symbol* sym = npi_->sym_->u.ppsym[i]; if (strcmp(sym->name, name) == 0) { - return sym; + rsym = sym; + break; } } - return 0; + return rsym; } -int NrnProperty::prop_index(const Symbol* s) const { + +neuron::container::data_handle NrnProperty::pval(const Symbol* s, int index) { assert(s); if (s->type != RANGEVAR && s->type != RANGEOBJ) { hoc_execerror(s->name, "not a range variable"); } - return s->u.rng.index; -} - -neuron::container::data_handle NrnProperty::prop_pval(const Symbol* s, int index) const { - if (npi_->p_->ob) { - return neuron::container::data_handle{ - npi_->p_->ob->u.dataspace[prop_index(s)].pval + index}; - } else { - if (s->subtype == NRNPOINTER) { - return static_cast>( - npi_->p_->dparam[prop_index(s) + index]); - } else { - return npi_->p_->param_handle_legacy(prop_index(s) + index); - } - } + double* raw = &npi_->params_[s->u.rng.index + index]; + return static_cast>(raw); } //-------------------------------------------------- @@ -269,34 +287,3 @@ Object* SectionList::nrn_object() { Object* ob = sli_->ob_; return ob; } - -#if 0 -//--------------------------------------------------- -NrnSection::NrnSection(Section* sec) { - npi_ = new NrnSectionImpl(sec); -} -NrnSection::~NrnSection() { - delete npi_; -} -void NrnSection::section(Section* sec) { - section_ref(sec); - section_unref(npi_->sec_); - npi_->sec_ = sec; -} -Section* NrnSection::section() { - return npi_->sec_; -} - -Node* NrnSection::node(int inode) { return sec_->pnode[inode]; } - -bool NrnSection::is_mechanism(type) { - return nrn_mechanism(type, node(0)) != (Prop*)0 -} - -double* NrnSection::var_pointer(const char* var) { - nrn_pushsec(nsi_->sec_); - double* pval = hoc_val_pointer(var); - nrn_popsec(); - return pval; -} -#endif diff --git a/src/nrniv/ndatclas.h b/src/nrniv/ndatclas.h index b10e9c3b13..83f3ab0acb 100644 --- a/src/nrniv/ndatclas.h +++ b/src/nrniv/ndatclas.h @@ -9,26 +9,21 @@ class NrnNodeImpl; class NrnProperty { public: - NrnProperty(Prop*); NrnProperty(const char*); virtual ~NrnProperty(); const char* name() const; int type() const; - bool is_point() const; bool deleteable(); Symbol* first_var(); bool more_var(); Symbol* next_var(); - Symbol* find(const char* rangevar); + Symbol* findsym(const char* rangevar); Symbol* var(int); - int prop_index(const Symbol*) const; - neuron::container::data_handle prop_pval(const Symbol*, int arrayindex = 0) const; - - Prop* prop() const; - int var_type(Symbol*) const; + neuron::container::data_handle pval(const Symbol*, int index); // vartype=0, 1, 2, 3 means all, PARAMETER, ASSIGNED, STATE - static bool assign(Prop* src, Prop* dest, int vartype = 0); + bool copy(bool to_prop, Prop* dest, Node* nd_dest, int vartype = 0); + bool copy_out(NrnProperty& dest, int vartype = 0); private: NrnPropertyImpl* npi_; @@ -46,20 +41,4 @@ class SectionList: public Resource { SectionListImpl* sli_; }; -#if 0 -class NrnSection { -public: - NrnSection(Section* sec); - virtual ~NrnSection(); - void section(Section*); - Section* section(); - Node* node(int index); - bool is_mechanism(int type); - double* var_pointer(Symbol*, int index=0, int inode=0); - double* var_pointer(const char*); // eg. ca_cadifus[2](.7) -private: - NrnNodeImpl* nni_; -}; -#endif - #endif diff --git a/src/nrniv/nrnmenu.cpp b/src/nrniv/nrnmenu.cpp index 463ab45280..f771e9fb44 100644 --- a/src/nrniv/nrnmenu.cpp +++ b/src/nrniv/nrnmenu.cpp @@ -620,6 +620,11 @@ static double ms_count(void* v) { hoc_return_type_code = 1; return ((MechanismStandard*) v)->count(); } +static double ms_is_array(void* v) { + auto* ms = static_cast(v); + hoc_return_type_code = 2; + return ms->is_array((int) chkarg(1, 0, ms->count() - 1)); +} static double ms_name(void* v) { const char* n; int rval = 0; @@ -668,6 +673,7 @@ static Member_func ms_members[] = {{"panel", ms_panel}, {"set", ms_set}, {"get", ms_get}, {"count", ms_count}, + {"is_array", ms_is_array}, {"name", ms_name}, {"save", ms_save}, {0, 0}}; @@ -706,7 +712,7 @@ MechanismStandard::MechanismStandard(const char* name, int vartype) { } } else { for (Symbol* sym = np_->first_var(); np_->more_var(); sym = np_->next_var()) { - int type = np_->var_type(sym); + int type = nrn_vartype(sym); if (type < vartype) { ++offset_; } else if (vartype == 0 || type == vartype) { @@ -729,10 +735,14 @@ MechanismStandard::~MechanismStandard() { int MechanismStandard::count() { return name_cnt_; } -const char* MechanismStandard::name() { +bool MechanismStandard::is_array(int i) const { + const Symbol* s = np_->var(i + offset_); + return s->arayinfo; +} +const char* MechanismStandard::name() const { return np_->name(); } -const char* MechanismStandard::name(int i, int& size) { +const char* MechanismStandard::name(int i, int& size) const { Symbol* s; if (vartype_ == -1) { s = glosym_[i]; @@ -756,7 +766,7 @@ void MechanismStandard::panel(const char* label) { hoc_ivlabel(np_->name()); } for (sym = np_->first_var(), i = 0; np_->more_var(); sym = np_->next_var(), ++i) { - if (vartype_ == 0 || np_->var_type(sym) == vartype_) { + if (vartype_ == 0 || nrn_vartype(sym) == vartype_) { Object* pyactval = NULL; int size = hoc_total_array_data(sym, 0); if (pyact_) { @@ -770,7 +780,7 @@ void MechanismStandard::panel(const char* label) { } hoc_ivvaluerun_ex(sym->name, NULL, - np_->prop_pval(sym), + np_->pval(sym, 0), NULL, pyact_ ? NULL : buf, pyactval, @@ -797,7 +807,7 @@ void MechanismStandard::panel(const char* label) { Sprintf(buf2, "%s[%d]", sym->name, j); hoc_ivvaluerun_ex(buf2, NULL, - np_->prop_pval(sym, j), + np_->pval(sym, j), NULL, pyact_ ? NULL : buf, pyact_, @@ -824,20 +834,20 @@ void MechanismStandard::action(const char* action, Object* pyact) { } void MechanismStandard::set(const char* name, double val, int index) { mschk("set"); - Symbol* s = np_->find(name); + const Symbol* s = np_->findsym(name); if (s) { - *np_->prop_pval(s, index) = val; + *np_->pval(s, index) = val; } else { hoc_execerror(name, "not in this property"); } } double MechanismStandard::get(const char* name, int index) { mschk("get"); - Symbol* s = np_->find(name); + const Symbol* s = np_->findsym(name); if (!s) { hoc_execerror(name, "not in this property"); } - auto const pval = np_->prop_pval(s, index); + auto const pval = np_->pval(s, index); if (!pval) { return -1e300; } @@ -851,15 +861,15 @@ void MechanismStandard::in(Section* sec, double x) { i = node_index(sec, x); } Prop* p = nrn_mechanism(np_->type(), sec->pnode[i]); - NrnProperty::assign(p, np_->prop(), vartype_); + np_->copy(false, p, sec->pnode[i], vartype_); } void MechanismStandard::in(Point_process* pp) { mschk("in"); - NrnProperty::assign(pp->prop, np_->prop(), vartype_); + np_->copy(false, pp->prop, pp->node, vartype_); } void MechanismStandard::in(MechanismStandard* ms) { mschk("in"); - NrnProperty::assign(ms->np_->prop(), np_->prop(), vartype_); + ms->np_->copy_out(*np_, vartype_); } void MechanismStandard::out(Section* sec, double x) { @@ -867,21 +877,21 @@ void MechanismStandard::out(Section* sec, double x) { if (x < 0) { for (int i = 0; i < sec->nnode; ++i) { Prop* p = nrn_mechanism(np_->type(), sec->pnode[i]); - NrnProperty::assign(np_->prop(), p, vartype_); + np_->copy(true, p, sec->pnode[i], vartype_); } } else { int i = node_index(sec, x); Prop* p = nrn_mechanism(np_->type(), sec->pnode[i]); - NrnProperty::assign(np_->prop(), p, vartype_); + np_->copy(true, p, sec->pnode[i], vartype_); } } void MechanismStandard::out(Point_process* pp) { mschk("out"); - NrnProperty::assign(np_->prop(), pp->prop, vartype_); + np_->copy(true, pp->prop, pp->node, vartype_); } void MechanismStandard::out(MechanismStandard* ms) { mschk("out"); - NrnProperty::assign(np_->prop(), ms->np_->prop(), vartype_); + np_->copy_out(*ms->np_, vartype_); } void MechanismStandard::save(const char* obref, std::ostream* po) { @@ -891,11 +901,10 @@ void MechanismStandard::save(const char* obref, std::ostream* po) { Sprintf(buf, "%s = new MechanismStandard(\"%s\")", obref, np_->name()); o << buf << std::endl; for (Symbol* sym = np_->first_var(); np_->more_var(); sym = np_->next_var()) { - if (vartype_ == 0 || np_->var_type(sym) == vartype_) { + if (vartype_ == 0 || nrn_vartype(sym) == vartype_) { int i, cnt = hoc_total_array_data(sym, 0); for (i = 0; i < cnt; ++i) { - Sprintf( - buf, "%s.set(\"%s\", %g, %d)", obref, sym->name, *np_->prop_pval(sym, i), i); + Sprintf(buf, "%s.set(\"%s\", %g, %d)", obref, sym->name, *np_->pval(sym, i), i); o << buf << std::endl; } } diff --git a/src/nrniv/nrnmenu.h b/src/nrniv/nrnmenu.h index f310394990..d55cd0557e 100644 --- a/src/nrniv/nrnmenu.h +++ b/src/nrniv/nrnmenu.h @@ -13,8 +13,9 @@ class MechanismStandard: public Resource { void action(const char*, Object* pyact); int count(); - const char* name(); - const char* name(int, int&); // returns array dimension and name + const char* name() const; + const char* name(int, int&) const; // returns array dimension and name + bool is_array(int) const; // from arg (section.node(x) (0 if x < 0) to this void in(Section*, double x = -1.); diff --git a/src/nrniv/savstate.cpp b/src/nrniv/savstate.cpp index f50473cbba..50b1175c5d 100644 --- a/src/nrniv/savstate.cpp +++ b/src/nrniv/savstate.cpp @@ -7,7 +7,6 @@ #include "nrncvode.h" #include "nrnoc2iv.h" #include "classreg.h" -#include "ndatclas.h" #include "nrniv_mf.h" #include "tqueue.hpp" @@ -232,14 +231,12 @@ void SaveState::ssi_def() { Symbol* s = hoc_lookup("NetCon"); nct = s->u.ctemplate; ssi = new StateStructInfo[n_memb_func]; - int sav = v_structure_change; for (int im = 0; im < n_memb_func; ++im) { ssi[im].offset = -1; ssi[im].size = 0; if (!memb_func[im].sym) { continue; } - NrnProperty* np = new NrnProperty(memb_func[im].sym->name); // generally we only save STATE variables. However for // models containing a NET_RECEIVE block, we also need to // save everything except the parameters @@ -250,28 +247,26 @@ void SaveState::ssi_def() { // param array including PARAMETERs. if (pnt_receive[im]) { ssi[im].offset = 0; - ssi[im].size = np->prop()->param_size(); // sum over array dimensions + ssi[im].size = nrn_prop_param_size_[im]; } else { int type = STATE; - for (Symbol* sym = np->first_var(); np->more_var(); sym = np->next_var()) { - if (np->var_type(sym) == type || np->var_type(sym) == STATE || - sym->subtype == _AMBIGUOUS) { + const Symbol* msym = memb_func[im].sym; + for (int i = 0; i < msym->s_varn; ++i) { + Symbol* sym = msym->u.ppsym[i]; + int vartype = nrn_vartype(sym); + if (vartype == type || vartype == STATE || vartype == _AMBIGUOUS) { if (ssi[im].offset < 0) { - ssi[im].offset = np->prop_index(sym); + ssi[im].offset = sym->u.rng.index; } else { // assert what we assume: that after this code the variables we want are // `size` contiguous legacy indices starting at `offset` - assert(ssi[im].offset + ssi[im].size == np->prop_index(sym)); + assert(ssi[im].offset + ssi[im].size == sym->u.rng.index); } ssi[im].size += hoc_total_array_data(sym, 0); } } } - delete np; } - // Following set to 1 when NrnProperty constructor calls prop_alloc. - // so change back to original value. - v_structure_change = sav; } bool SaveState::check(bool warn) { diff --git a/src/nrnoc/capac.cpp b/src/nrnoc/capac.cpp index 7ffbb3629e..8211c15a2c 100644 --- a/src/nrnoc/capac.cpp +++ b/src/nrnoc/capac.cpp @@ -13,11 +13,14 @@ static void cap_init(neuron::model_sorted_token const&, NrnThread*, Memb_list*, static constexpr auto nparm = 2; static constexpr auto ndparm = 0; +static std::vector parm_default{DEF_cm}; + extern "C" void capac_reg_(void) { int mechtype; /* all methods deal with capacitance in special ways */ register_mech(mechanism, cap_alloc, nullptr, nullptr, nullptr, cap_init, -1, 1); mechtype = nrn_get_mechtype(mechanism[1]); + hoc_register_parm_default(mechtype, &parm_default); using neuron::mechanism::field; neuron::mechanism::register_data_fields(mechtype, field{"cm"}, field{"i_cap"}); hoc_register_prop_size(mechtype, nparm, 0); @@ -111,5 +114,5 @@ void nrn_div_capacity(neuron::model_sorted_token const& sorted_token, static void cap_alloc(Prop* p) { assert(p->param_size() == nparm); assert(p->param_num_vars() == nparm); - p->param(0) = DEF_cm; // default capacitance/cm^2 + p->param(0) = parm_default[0]; // DEF_cm default capacitance/cm^2 } diff --git a/src/nrnoc/eion.cpp b/src/nrnoc/eion.cpp index 602eac5e58..908ff06683 100644 --- a/src/nrnoc/eion.cpp +++ b/src/nrnoc/eion.cpp @@ -147,6 +147,19 @@ void ion_charge(void) { hoc_retpushx(global_charge(s->subtype)); } +static std::vector naparmdflt{DEF_ena, DEF_nai, DEF_nao}; +static std::vector kparmdflt{DEF_ek, DEF_ki, DEF_ko}; +static std::vector caparmdflt{DEF_eca, DEF_cai, DEF_cao}; +static std::vector ionparmdflt{DEF_eion, DEF_ioni, DEF_iono}; + +void reg_parm_default(int mechtype, const std::string& name) { + if (name == "na") { + hoc_register_parm_default(mechtype, &naparmdflt); + } else { + hoc_register_parm_default(mechtype, &ionparmdflt); + } +} + void ion_reg(const char* name, double valence) { int i, mechtype; Symbol* s; @@ -176,6 +189,7 @@ void ion_reg(const char* name, double valence) { hoc_symbol_units(hoc_lookup(buf[6].c_str()), "S/cm2"); s = hoc_lookup(buf[0].c_str()); mechtype = nrn_get_mechtype(mechanism[1]); + reg_parm_default(mechtype, name_s); using neuron::mechanism::field; neuron::mechanism::register_data_fields(mechtype, field{buf[1]}, // erev @@ -478,7 +492,7 @@ void ion_style(void) { hoc_retpushx((double) oldstyle); } -int nrn_vartype(Symbol* sym) { +int nrn_vartype(const Symbol* sym) { int i; i = sym->subtype; if (i == _AMBIGUOUS) { @@ -595,21 +609,21 @@ static void ion_alloc(Prop* p) { p->param(cur_index) = 0.; p->param(dcurdv_index) = 0.; if (p->_type == na_ion) { - p->param(erev_index) = DEF_ena; - p->param(conci_index) = DEF_nai; - p->param(conco_index) = DEF_nao; + p->param(erev_index) = naparmdflt[0]; + p->param(conci_index) = naparmdflt[1]; + p->param(conco_index) = naparmdflt[2]; } else if (p->_type == k_ion) { - p->param(erev_index) = DEF_ek; - p->param(conci_index) = DEF_ki; - p->param(conco_index) = DEF_ko; + p->param(erev_index) = kparmdflt[0]; + p->param(conci_index) = kparmdflt[1]; + p->param(conco_index) = kparmdflt[2]; } else if (p->_type == ca_ion) { - p->param(erev_index) = DEF_eca; - p->param(conci_index) = DEF_cai; - p->param(conco_index) = DEF_cao; + p->param(erev_index) = caparmdflt[0]; + p->param(conci_index) = caparmdflt[1]; + p->param(conco_index) = caparmdflt[2]; } else { - p->param(erev_index) = DEF_eion; - p->param(conci_index) = DEF_ioni; - p->param(conco_index) = DEF_iono; + p->param(erev_index) = ionparmdflt[0]; + p->param(conci_index) = ionparmdflt[1]; + p->param(conco_index) = ionparmdflt[2]; } p->dparam = nrn_prop_datum_alloc(p->_type, ndparam, p); p->dparam[iontype_index_dparam] = 0; diff --git a/src/nrnoc/extcelln.cpp b/src/nrnoc/extcelln.cpp index 674e89474d..61eb0900a7 100644 --- a/src/nrnoc/extcelln.cpp +++ b/src/nrnoc/extcelln.cpp @@ -89,10 +89,18 @@ static void update_parmsize() { hoc_register_prop_size(EXTRACELL, nparm, 0); } +static std::vector param_default{ + 1e9, /* xraxial */ + 1e9, /* xg */ + 0.0, /* xc */ + 0.0, /* e_extracellular */ +}; + extern "C" void extracell_reg_(void) { register_mech(mechanism, extcell_alloc, nullptr, nullptr, nullptr, extcell_init, -1, 1); int const i = nrn_get_mechtype(mechanism[1]); assert(i == EXTRACELL); + hoc_register_parm_default(EXTRACELL, ¶m_default); hoc_register_cvode(i, _ode_count, nullptr, nullptr, nullptr); hoc_register_limits(i, limits); hoc_register_units(i, units); @@ -174,11 +182,11 @@ static void extcell_alloc(Prop* p) { assert(p->param_size() == (nparm - 3) + 3 * nrn_nlayer_extracellular); assert(p->param_num_vars() == nparm); for (auto i = 0; i < nrn_nlayer_extracellular; ++i) { - p->param(xraxial_index, i) = 1e9; - p->param(xg_index, i) = 1e9; - p->param(xc_index, i) = 0.0; + p->param(xraxial_index, i) = param_default[0]; + p->param(xg_index, i) = param_default[1]; + p->param(xc_index, i) = param_default[2]; } - p->param(e_extracellular_index) = 0.0; + p->param(e_extracellular_index) = param_default[3]; } /*ARGSUSED*/ diff --git a/src/nrnoc/init.cpp b/src/nrnoc/init.cpp index 0b09f07af4..ce86426495 100644 --- a/src/nrnoc/init.cpp +++ b/src/nrnoc/init.cpp @@ -112,6 +112,7 @@ int nrn_global_ncell = 0; /* used to be rootnodecount */ extern double hoc_default_dll_loaded_; extern int nrn_istty_; extern int nrn_nobanner_; +static std::vector morph_parm_default{DEF_diam}; static HocParmLimits _hoc_parm_limits[] = {{"Ra", {1e-6, 1e9}}, {"L", {1e-4, 1e20}}, @@ -383,6 +384,7 @@ void hoc_last_init(void) { SectionList_reg(); SectionRef_reg(); register_mech(morph_mech, morph_alloc, nullptr, nullptr, nullptr, nullptr, -1, 0); + hoc_register_parm_default(MORPHOLOGY, &morph_parm_default); neuron::mechanism::register_data_fields(MORPHOLOGY, neuron::mechanism::field{"diam"}); hoc_register_prop_size(MORPHOLOGY, 1, 0); for (m = mechanism; *m; m++) { @@ -747,6 +749,10 @@ void register_mech(const char** m, } } +void hoc_register_parm_default(int mechtype, const std::vector* pd) { + memb_func[mechtype].parm_default = pd; +} + void nrn_writes_conc(int mechtype, int unused) { static int lastion = EXTRACELL + 1; int i; @@ -849,6 +855,25 @@ void update_mech_ppsym_for_modlrandom( } // namespace namespace neuron::mechanism::detail { + +// Use this if string.c_str() causes possibility of +// AddressSanitizer: stack-use-after-scope on address +void register_data_fields(int mechtype, + std::vector> const& param_info, + std::vector> const& dparam_info) { + std::vector> params{}; + std::vector> dparams{}; + + for (auto&& [str, i]: param_info) { + params.emplace_back(str.c_str(), i); + } + for (auto&& [str1, str2]: dparam_info) { + dparams.emplace_back(str1.c_str(), str2.c_str()); + } + + register_data_fields(mechtype, params, dparams); +} + void register_data_fields(int mechtype, std::vector> const& param_info, std::vector> const& dparam_info) { diff --git a/src/nrnoc/membfunc.h b/src/nrnoc/membfunc.h index 5cf6ca55bf..ff980c0427 100644 --- a/src/nrnoc/membfunc.h +++ b/src/nrnoc/membfunc.h @@ -84,7 +84,8 @@ struct Memb_func { int is_point; void* hoc_mech; void (*setdata_)(struct Prop*); - int* dparam_semantics; // for nrncore writing. + int* dparam_semantics; // for nrncore writing. + const std::vector* parm_default; // for NrnProperty private: nrn_init_t m_initialize{}; }; @@ -106,6 +107,8 @@ using NPyDirectMechFuncs = std::unordered_map; extern void hoc_register_npy_direct(int type, NPyDirectMechFunc*); extern std::unordered_map nrn_mech2funcs_map; +extern void hoc_register_parm_default(int type, const std::vector*); + #define IMEMFAST -2 #define VINDEX -1 #define CABLESECTION 1 @@ -207,6 +210,9 @@ namespace detail { void register_data_fields(int mech_type, std::vector> const& param_info, std::vector> const& dparam_size); +void register_data_fields(int mech_type, + std::vector> const& param_info, + std::vector> const& dparam_size); } // namespace detail /** * @brief Type- and array-aware version of hoc_register_prop_size. diff --git a/src/nrnoc/multicore.cpp b/src/nrnoc/multicore.cpp index ac2dbdd70e..4f143d3cf0 100644 --- a/src/nrnoc/multicore.cpp +++ b/src/nrnoc/multicore.cpp @@ -608,21 +608,29 @@ printf("thread_memblist_setup %lx v_node_count=%d ncell=%d end=%d\n", (long)nth, } /* fill in the Point_process._vnt value. */ /* artificial cells done in v_setup_vectors() */ - for (tml = _nt->tml; tml; tml = tml->next) - if (memb_func[tml->index].is_point) { - for (i = 0; i < tml->ml->nodecount; ++i) { + for (tml = _nt->tml; tml; tml = tml->next) { + if (!memb_func[tml->index].is_point) { + continue; + } + if (memb_func[tml->index].hoc_mech) { + // I don't think hoc_mech works with multiple threads. + for (int i = 0; i < tml->ml->nodecount; ++i) { + auto* pnt = tml->ml->prop[i]->dparam[1].get(); + pnt->_vnt = _nt; + } + } else { + for (int i = 0; i < tml->ml->nodecount; ++i) { auto* pnt = tml->ml->pdata[i][1].get(); pnt->_vnt = _nt; } } + } } void nrn_thread_memblist_setup() { - int it, *mlcnt; - void** vmap; - mlcnt = (int*) emalloc(n_memb_func * sizeof(int)); - vmap = (void**) emalloc(n_memb_func * sizeof(void*)); - for (it = 0; it < nrn_nthread; ++it) { + int* mlcnt = (int*) emalloc(n_memb_func * sizeof(int)); + void** vmap = (void**) emalloc(n_memb_func * sizeof(void*)); + for (int it = 0; it < nrn_nthread; ++it) { thread_memblist_setup(nrn_threads + it, mlcnt, vmap); } // Right now the sorting method updates the storage offsets inside the diff --git a/src/nrnoc/nrn_ansi.h b/src/nrnoc/nrn_ansi.h index 4435982870..c2cf668bb7 100644 --- a/src/nrnoc/nrn_ansi.h +++ b/src/nrnoc/nrn_ansi.h @@ -167,7 +167,7 @@ extern void nrn_clear_mark(void); extern short nrn_increment_mark(Section*); extern short nrn_value_mark(Section*); extern int is_point_process(Object*); -extern int nrn_vartype(Symbol*); // nrnocCONST, DEP, STATE +extern int nrn_vartype(const Symbol*); // nrnocCONST, DEP, STATE extern void recalc_diam(void); extern Prop* nrn_mechanism_check(int type, Section* sec, int inode); extern bool nrn_use_fast_imem; diff --git a/src/nrnoc/passive0.cpp b/src/nrnoc/passive0.cpp index b71f106666..e566176944 100644 --- a/src/nrnoc/passive0.cpp +++ b/src/nrnoc/passive0.cpp @@ -11,10 +11,13 @@ static void pas_alloc(Prop* p); static void pas_cur(neuron::model_sorted_token const&, NrnThread* nt, Memb_list* ml, int type); static void pas_jacob(neuron::model_sorted_token const&, NrnThread* nt, Memb_list* ml, int type); +static std::vector parm_default{DEF_g, DEF_e}; + extern "C" void passive0_reg_(void) { int mechtype; register_mech(mechanism, pas_alloc, pas_cur, pas_jacob, nullptr, nullptr, -1, 1); mechtype = nrn_get_mechtype(mechanism[1]); + hoc_register_parm_default(mechtype, &parm_default); using neuron::mechanism::field; neuron::mechanism::register_data_fields(mechtype, field{"g_fastpas"}, @@ -46,6 +49,6 @@ static void pas_jacob(neuron::model_sorted_token const&, NrnThread* nt, Memb_lis static void pas_alloc(Prop* p) { assert(p->param_size() == nparm); - p->param(0) = DEF_g; - p->param(1) = DEF_e; + p->param(0) = parm_default[0]; // DEF_g + p->param(1) = parm_default[1]; // DEF_e } diff --git a/src/nrnpython/nrnpy_nrn.cpp b/src/nrnpython/nrnpy_nrn.cpp index 4635c35e25..f8d482a992 100644 --- a/src/nrnpython/nrnpy_nrn.cpp +++ b/src/nrnpython/nrnpy_nrn.cpp @@ -2137,6 +2137,38 @@ static bool striptrail(char* buf, int sz, const char* n, const char* m) { return false; } +static Symbol* var_find_in_mech(Symbol* mech, const char* varname) { + int cnt = mech->s_varn; + for (int i = 0; i < cnt; ++i) { + Symbol* sym = mech->u.ppsym[i]; + if (strcmp(sym->name, varname) == 0) { + return sym; + } + } + return nullptr; +} + +static neuron::container::data_handle var_pval(NPyMechObj* pymech, + Symbol* symvar, + int index) { + if (pymech->prop_->ob) { // HocMech created by make_mechanism + Object* ob = pymech->prop_->ob; + // strip suffix + std::string s = symvar->name; + std::string suffix{"_"}; + suffix += memb_func[pymech->type_].sym->name; + s.resize(s.rfind(suffix)); + + Symbol* sym = hoc_table_lookup(s.c_str(), ob->ctemplate->symtable); + assert(sym); + double* pd = ob->u.dataspace[sym->u.rng.index].pval + index; + return neuron::container::data_handle{pd}; + } + int sym_index = symvar->u.rng.index; + auto dh = pymech->prop_->param_handle_legacy(sym_index + index); + return dh; +} + static PyObject* mech_getattro(NPyMechObj* self, PyObject* pyname) { Section* sec = self->pyseg_->pysec_->sec_; if (!sec->prop) { @@ -2154,9 +2186,9 @@ static PyObject* mech_getattro(NPyMechObj* self, PyObject* pyname) { } // printf("mech_getattro %s\n", n); PyObject* result = NULL; - NrnProperty np(self->prop_); int isptr = (strncmp(n, "_ref_", 5) == 0); - char* mname = memb_func[self->prop_->_type].sym->name; + Symbol* mechsym = memb_func[self->type_].sym; + char* mname = mechsym->name; int mnamelen = strlen(mname); int bufsz = strlen(n) + mnamelen + 2; char* buf = new char[bufsz]; @@ -2165,7 +2197,7 @@ static PyObject* mech_getattro(NPyMechObj* self, PyObject* pyname) { } else { std::snprintf(buf, bufsz, "%s_%s", isptr ? n + 5 : n, mname); } - Symbol* sym = np.find(buf); + Symbol* sym = var_find_in_mech(mechsym, buf); if (sym && sym->type == RANGEVAR) { // printf("mech_getattro sym %s\n", sym->name); if (ISARRAY(sym)) { @@ -2177,7 +2209,7 @@ static PyObject* mech_getattro(NPyMechObj* self, PyObject* pyname) { r->attr_from_sec_ = 0; result = (PyObject*) r; } else { - auto const px = np.prop_pval(sym, 0); + auto const px = var_pval(self, sym, 0); if (!px) { rv_noexist(sec, sym->name, self->pyseg_->x_, 2); } else if (isptr) { @@ -2191,7 +2223,9 @@ static PyObject* mech_getattro(NPyMechObj* self, PyObject* pyname) { result = nrnpy_ho2po(ob); } else if (strcmp(n, "__dict__") == 0) { result = PyDict_New(); - for (Symbol* s = np.first_var(); np.more_var(); s = np.next_var()) { + int cnt = mechsym->s_varn; + for (int i = 0; i < cnt; ++i) { + Symbol* s = mechsym->u.ppsym[i]; if (!striptrail(buf, bufsz, s->name, mname)) { strcpy(buf, s->name); } @@ -2243,9 +2277,9 @@ static int mech_setattro(NPyMechObj* self, PyObject* pyname, PyObject* value) { return -1; } // printf("mech_setattro %s\n", n); - NrnProperty np(self->prop_); int isptr = (strncmp(n, "_ref_", 5) == 0); - char* mname = memb_func[self->prop_->_type].sym->name; + Symbol* mechsym = memb_func[self->type_].sym; + char* mname = mechsym->name; int mnamelen = strlen(mname); int bufsz = strlen(n) + mnamelen + 2; char* buf = new char[bufsz]; @@ -2254,14 +2288,14 @@ static int mech_setattro(NPyMechObj* self, PyObject* pyname, PyObject* value) { } else { std::snprintf(buf, bufsz, "%s_%s", isptr ? n + 5 : n, mname); } - Symbol* sym = np.find(buf); + Symbol* sym = var_find_in_mech(mechsym, buf); delete[] buf; if (sym) { if (isptr) { err = nrn_pointer_assign(self->prop_, sym, value); } else { double x; - auto pd = np.prop_pval(sym, 0); + auto pd = var_pval(self, sym, 0); if (pd) { if (PyArg_Parse(value, "d", &x) == 1) { *pd = x; @@ -2286,19 +2320,19 @@ neuron::container::generic_data_handle* nrnpy_setpointer_helper(PyObject* pyname return nullptr; } NPyMechObj* m = (NPyMechObj*) mech; - NrnProperty np(m->prop_); + Symbol* msym = memb_func[m->type_].sym; char buf[200]; Py2NRNString name(pyname); char* n = name.c_str(); if (!n) { return nullptr; } - Sprintf(buf, "%s_%s", n, memb_func[m->prop_->_type].sym->name); - Symbol* sym = np.find(buf); + Sprintf(buf, "%s_%s", n, msym->name); + Symbol* sym = var_find_in_mech(msym, buf); if (!sym || sym->type != RANGEVAR || sym->subtype != NRNPOINTER) { return nullptr; } - return &(m->prop_->dparam[np.prop_index(sym)]); + return &(m->prop_->dparam[sym->u.rng.index]); } static PyObject* NPySecObj_call(NPySecObj* self, PyObject* args) { diff --git a/src/oc/hoc_oop.cpp b/src/oc/hoc_oop.cpp index c56f418654..6a3e706f02 100644 --- a/src/oc/hoc_oop.cpp +++ b/src/oc/hoc_oop.cpp @@ -88,7 +88,7 @@ size_t hoc_total_array(Symbol* s) /* total number of elements in array pointer * return total; } -size_t hoc_total_array_data(Symbol* s, +size_t hoc_total_array_data(const Symbol* s, Objectdata* obd) /* total number of elements in array pointer */ { Arrayinfo* a; diff --git a/src/oc/oc_ansi.h b/src/oc/oc_ansi.h index 1499f77512..afebcfd105 100644 --- a/src/oc/oc_ansi.h +++ b/src/oc/oc_ansi.h @@ -299,7 +299,7 @@ template T const& hoc_look_inside_stack(int i); Object* hoc_obj_look_inside_stack(int); void hoc_stkobj_unref(Object*, int stkindex); -size_t hoc_total_array_data(Symbol*, Objectdata*); +size_t hoc_total_array_data(const Symbol*, Objectdata*); char* hoc_araystr(Symbol*, int, Objectdata*); char* hoc_object_pathname(Object*); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 86d011562b..bb07cc86d7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,7 +23,8 @@ add_executable( unit_tests/container/mechanism.cpp unit_tests/container/node.cpp unit_tests/utils/enumerate.cpp - unit_tests/oc/hoc_interpreter.cpp) + unit_tests/oc/hoc_interpreter.cpp + cover/unit_tests/cover.cpp) set(catch2_targets testneuron) if(NRN_ENABLE_THREADS) add_executable(nrn-benchmarks common/catch2_main.cpp benchmarks/threads/test_multicore.cpp) diff --git a/test/cover/unit_tests/cover.cpp b/test/cover/unit_tests/cover.cpp new file mode 100644 index 0000000000..c1c2945b57 --- /dev/null +++ b/test/cover/unit_tests/cover.cpp @@ -0,0 +1,41 @@ +#include <../../nrnconf.h> +#include "oc_ansi.h" + +extern bool hoc_valid_stmt(const char*, Object*); + +#include + +// Help cover PR's + +// #2675 NrnProperty handles extracellular and no longer creates/wraps a Prop structure. +// 8 missing lines in Codecov Report +// Difficult to cover with the interpreter as it requires manual mouse +// interaction with a SymbolChooser or "Plot what?" of Graph. +#include "symdir.h" +TEST_CASE("Cover #2675 ivoc/symdir.cpp", "[SymDirectory]") { + REQUIRE(hoc_valid_stmt( + "" + "// Create a POINT_PROCESS sufficient to cover SymDirectoryImpl::load_mechanism\n" + "begintemplate Mech1\n" + " public a, b, c\n" + " double b[3], c[8]\n" + " a = 0\n" + "endtemplate Mech1\n" + "make_mechanism(\"mech1\", \"Mech1\")\n" + "create soma\n" + "insert mech1\n" + "objref ic\n" + "ic = new IClamp(.5) // just to cover one line.\n", + nullptr) == 1); + std::string parent_path{""}; + auto* somasym = hoc_lookup("soma"); + REQUIRE(somasym); + auto symdir = new SymDirectory(parent_path, nullptr, somasym, 0, 0); + for (int i = 0; i < symdir->count(); ++i) { + printf("%d %s\n", i, symdir->name(i).c_str()); + } + REQUIRE(symdir->count() == 10); + REQUIRE(symdir->name(4) == "c_mech1[all]( 0.5 )"); + delete symdir; + REQUIRE(hoc_valid_stmt("objref ic\nsoma { delete_section() }", nullptr) == 1); +} diff --git a/test/hoctests/tests/test_bbss.py b/test/hoctests/tests/test_bbss.py new file mode 100644 index 0000000000..32403bed31 --- /dev/null +++ b/test/hoctests/tests/test_bbss.py @@ -0,0 +1,39 @@ +from neuron import h + + +class Cell: + def __init__(self): + self.s = h.Section("soma", self) + self.s.insert("extracellular") + + +def bbsave(): + h.finitialize(-65) # segfault if not present + bbss = h.BBSaveState() + bbss.save("temp.bbss") + + +def test_nocell(): # not wrapped in cell (no output in temp.bbss) + s = h.Section("soma") + s.insert("extracellular") + bbsave() + + +def test_cell1(): # no gid (no output in temp.bbss) + cell = Cell() + bbsave() + + +def test_cell2(): + pc = h.ParallelContext() + cell = Cell() + gid = 1 + pc.set_gid2node(gid, pc.id()) + pc.cell(gid, h.NetCon(cell.s(0.5)._ref_v, None)) + bbsave() + + +if __name__ == "__main__": + test_nocell() + test_cell1() + test_cell2() diff --git a/test/hoctests/tests/test_hocmech.py b/test/hoctests/tests/test_hocmech.py new file mode 100644 index 0000000000..af9f1af1f2 --- /dev/null +++ b/test/hoctests/tests/test_hocmech.py @@ -0,0 +1,86 @@ +from neuron import h, gui +from neuron.expect_hocerr import expect_err, set_quiet + +set_quiet(False) +import math + +soma = h.Section(name="soma") +soma.L = soma.diam = math.sqrt(100 / math.pi) +soma.insert("hh") # equivalently: soma.insert(h.hh) + +stim = h.IClamp(soma(0.5)) +stim.dur = 0.1 +stim.amp = 0.3 + +# declare a density mechanism using HOC +h( + """ + begintemplate Max + public V, a, b + a = 0 + double b[2] + + proc initial() { + V = v($1) + } + + proc after_step() { + if (V < v($1)) { + V = v($1) + } + } + endtemplate Max +""" +) +h.make_mechanism("max", "Max", "a b") + +# declare a POINT_PROCESS using HOC +h( + """ + begintemplate PMax + public V, c, d + c = 0 + double d[2] + + proc initial() { + V = v($1) + } + + proc after_step() { + if (V < v($1)) { + V = v($1) + } + } + endtemplate PMax +""" +) +pm = h.PMax() +expect_err('h.make_pointprocess("PMax", "c d")') +del pm +h.make_pointprocess("PMax", "c d") + +soma.insert("max") +pm = h.PMax(0.5, sec=soma) +pm = h.PMax(soma(0.5)) + +h.run() + +print("V_max = %g" % soma(0.5).V_max) +print("max.V = %g" % soma(0.5).max.V) +print("pm.V = %g" % pm.V) + +expect_err('h.make_mechanism("max", "Max")') +assert pm.has_loc() == True +expect_err("pm.loc()") +pm.loc(soma(1.0)) +print(pm.get_loc(), h.secname()) + +mt = h.MechanismType(1) +mt.select("PMax") +pmaxref = h.ref(None) +mt.make(pmaxref) # eventually calls hoc_new_opoint +print(pmaxref[0]) +expect_err("pmaxref[0].get_loc()") +del mt, pmaxref + +expect_err('h.make_mechanism("xmax", "Max", "a 3")') diff --git a/test/hoctests/tests/test_kschan.json b/test/hoctests/tests/test_kschan.json index 911ad4fcd4..650c7a2faa 100644 --- a/test/hoctests/tests/test_kschan.json +++ b/test/hoctests/tests/test_kschan.json @@ -1,6 +1,6 @@ { "khh inserted": "c7fac0a91458c013fc1d958de60c4b82", - "khh same except for na_ion": "dd67b008494c0ad9cc15272e5d4dec5d", + "khh same": "c7fac0a91458c013fc1d958de60c4b82", "nahh now": "e347cfdcaf29a795d128c8e6d448ace7", "cb.ks.pr()": "7086b928209ac8d065c50226b66a9e92", "nahh cvode=True": [ @@ -1036,138 +1036,6 @@ 33.668960797953524 ] ], - "KSTrans cvode=True single=False": [ - [ - 0.0, - 0.002709768417113118, - 0.005419536834226236, - 0.012467737126800708, - 0.01951593741937518, - 0.026564137711949654, - 0.037929262088732305, - 0.055097845142384624, - 0.07226642819603694, - 0.08943501124968926, - 0.09999999999999779, - 0.10000000000000112, - 0.10291145582236776, - 0.1058229116447344, - 0.11307991550945612, - 0.12033691937417784, - 0.12759392323889956, - 0.1348509271036213, - 0.142107930968343, - 0.15627534804233148, - 0.17044276511631995, - 0.18461018219030842, - 0.1987775992642969, - 0.22903970883675928, - 0.2593018184092217, - 0.28956392798168407, - 0.31982603755414646, - 0.3387490256211881, - 0.3576720136882297, - 0.37659500175527133, - 0.4082425221547906, - 0.4398900425543099, - 0.4575896418581874, - 0.4752892411620649, - 0.4929888404659424, - 0.5106884397698199, - 0.5283880390736975, - 0.546087638377575, - 0.5583664363719241, - 0.5706452343662731, - 0.5829240323606222, - 0.5952028303549712, - 0.6074816283493203, - 0.6197604263436693, - 0.638740762004745, - 0.6512852258987768, - 0.6638296897928085, - 0.6763741536868403, - 0.688918617580872, - 0.7014630814749038, - 0.7203417997511373, - 0.7392205180273709, - 0.7725059776170974, - 0.795951860064221, - 0.8193977425113447, - 0.8523399062881889, - 0.8747121928093768, - 0.8970844793305648, - 0.9194567658517527, - 0.9418290523729407, - 0.9642013388941286, - 0.9865736254153166, - 1.0 - ], - [ - -65.0, - -64.1877602666191, - -63.37700436972419, - -61.27474347411282, - -59.181132492958355, - -57.09670622041916, - -53.754981303073464, - -48.75001932178034, - -43.79336850882914, - -38.87663940544805, - -35.866991165425695, - -35.866991165425695, - -35.91200372966857, - -35.955494472645746, - -36.057133989724306, - -36.14890908476081, - -36.22908175206404, - -36.296294096587005, - -36.349339650322335, - -36.40740587183988, - -36.39939402836742, - -36.31744169185362, - -36.153510434608705, - -35.493109152933705, - -34.345534786248635, - -32.62334558222328, - -30.223975751739708, - -28.326681257358466, - -26.080295194662764, - -23.4434938293419, - -18.04084473810003, - -11.239905426288574, - -6.783793705974767, - -1.880526831727696, - 3.403206016191021, - 8.944357511885288, - 14.559274064730252, - 20.016774744953846, - 23.577771396760312, - 26.867484841563723, - 29.825756650898406, - 32.41241607074128, - 34.61007657880926, - 36.423545772890414, - 38.52691260206173, - 39.513421346446485, - 40.23392115326073, - 40.734347045851486, - 41.05700949154364, - 41.23879945351103, - 41.31206517486708, - 41.21127720501345, - 40.75809586829616, - 40.29283191918134, - 39.73984910470677, - 38.84561439873791, - 38.17791860693501, - 37.4645617499352, - 36.70535557626778, - 35.90398401465756, - 35.06577588472098, - 34.19406272609299, - 33.65550633247177 - ] - ], "KSTrans cvode=False single=True": [ [ 0.0, @@ -1256,94 +1124,6 @@ 34.54936780351269 ] ], - "KSTrans cvode=False single=False": [ - [ - 0.0, - 0.025, - 0.05, - 0.075, - 0.09999999999999999, - 0.12499999999999999, - 0.15, - 0.17500000000000002, - 0.20000000000000004, - 0.22500000000000006, - 0.25000000000000006, - 0.2750000000000001, - 0.3000000000000001, - 0.3250000000000001, - 0.35000000000000014, - 0.37500000000000017, - 0.4000000000000002, - 0.4250000000000002, - 0.45000000000000023, - 0.47500000000000026, - 0.5000000000000002, - 0.5250000000000001, - 0.55, - 0.575, - 0.5999999999999999, - 0.6249999999999998, - 0.6499999999999997, - 0.6749999999999996, - 0.6999999999999995, - 0.7249999999999994, - 0.7499999999999993, - 0.7749999999999992, - 0.7999999999999992, - 0.8249999999999991, - 0.849999999999999, - 0.8749999999999989, - 0.8999999999999988, - 0.9249999999999987, - 0.9499999999999986, - 0.9749999999999985, - 0.9999999999999984 - ], - [ - -65.0, - -57.618094153388654, - -50.35331957004725, - -43.19554637871293, - -36.12533991235038, - -36.461609726084326, - -36.652196654645756, - -36.65387759522344, - -36.425824092027604, - -35.9289090786643, - -35.123988299281976, - -33.96953035937861, - -32.41893323890815, - -30.417901759760593, - -27.90244075129113, - -24.798447882793944, - -21.024706403093568, - -16.50236960890041, - -11.17546930330218, - -5.046865255555875, - 1.7718268027648048, - 9.01398290198184, - 16.253758465186838, - 22.98075273301613, - 28.74174378996713, - 33.273534063204586, - 36.54728935111428, - 38.71440952508067, - 40.00906435711161, - 40.66613162356864, - 40.878074673197304, - 40.784574163129875, - 40.479474091262134, - 40.02294019882796, - 39.45269777509161, - 38.792413422880266, - 38.057284770176736, - 37.257527143950945, - 36.40045933736523, - 35.49171880280428, - 34.53595780412696 - ] - ], "khh4 ivtype=0 ion=NonSpecific": [ [ 0.0, @@ -1871,5 +1651,219 @@ 32.21218074323528, 31.218680285698994 ] + ], + "KSTrans cvode=True single=False": [ + [ + 0.0, + 0.0027143066344790467, + 0.005428613268958093, + 0.012490885062614594, + 0.019553156856271094, + 0.026615428649927594, + 0.03800903502056763, + 0.05514693084159548, + 0.07228482666262333, + 0.08942272248365118, + 0.09999999999999779, + 0.10000000000000112, + 0.10291059502661276, + 0.1058211900532244, + 0.11307666574190492, + 0.12033214143058543, + 0.12758761711926594, + 0.13484309280794646, + 0.14209856849662697, + 0.15626681605027712, + 0.17043506360392727, + 0.18460331115757742, + 0.19877155871122756, + 0.2288718651320854, + 0.2589721715529433, + 0.28907247797380115, + 0.319172784394659, + 0.33809740253078896, + 0.3570220206669189, + 0.37594663880304885, + 0.4071537990821027, + 0.43836095936115654, + 0.45611081882296123, + 0.4738606782847659, + 0.4916105377465706, + 0.5093603972083753, + 0.5271102566701801, + 0.5448601161319848, + 0.5572067862725636, + 0.5695534564131424, + 0.5819001265537211, + 0.5942467966942999, + 0.6065934668348787, + 0.6189401369754575, + 0.6389905159039171, + 0.6516123149951849, + 0.6642341140864527, + 0.6768559131777205, + 0.6894777122689884, + 0.7020995113602562, + 0.7238128674546157, + 0.7455262235489752, + 0.7783127681601059, + 0.8110993127712365, + 0.8438858573823671, + 0.8766724019934977, + 0.9094589466046283, + 0.942245491215759, + 0.9750320358268896, + 1.0 + ], + [ + -65.0, + -64.18632124787062, + -63.37412627840421, + -61.26744702837232, + -59.16942160833874, + -57.080583970974544, + -53.730051257660435, + -48.73320285657006, + -43.78427161789913, + -38.87489559689218, + -35.86072873050872, + -35.86072873050872, + -35.905437080667824, + -35.94862357435213, + -36.04951236147748, + -36.140534448691284, + -36.21995098660866, + -36.28640319768453, + -36.33868356846626, + -36.395242127118244, + -36.385649479751955, + -36.302023146812715, + -36.13630078715495, + -35.476376110301636, + -34.33400867169064, + -32.622915894466445, + -30.24200806598848, + -28.349610945984633, + -26.108356637122288, + -23.476911880538534, + -18.16832383101533, + -11.501553783837052, + -7.060043936642613, + -2.1658871326786624, + 3.1167629524017313, + 8.667164901684119, + 14.302990988579486, + 19.792611242494218, + 23.390485966864674, + 26.718124053533902, + 29.71320134776253, + 32.33364271530464, + 34.56065987824687, + 36.39829031839922, + 38.61502871326616, + 39.59126444761296, + 40.30109185146664, + 40.79098727697746, + 41.10367999979603, + 41.27633866906626, + 41.33076687954793, + 41.17086391843039, + 40.670458301085205, + 39.962867314939935, + 39.10701234166909, + 38.13383073252198, + 37.06108604570682, + 35.90061431156987, + 34.66119695066269, + 33.66864036827043 + ] + ], + "KSTrans cvode=False single=False": [ + [ + 0.0, + 0.025, + 0.05, + 0.075, + 0.09999999999999999, + 0.12499999999999999, + 0.15, + 0.17500000000000002, + 0.20000000000000004, + 0.22500000000000006, + 0.25000000000000006, + 0.2750000000000001, + 0.3000000000000001, + 0.3250000000000001, + 0.35000000000000014, + 0.37500000000000017, + 0.4000000000000002, + 0.4250000000000002, + 0.45000000000000023, + 0.47500000000000026, + 0.5000000000000002, + 0.5250000000000001, + 0.55, + 0.575, + 0.5999999999999999, + 0.6249999999999998, + 0.6499999999999997, + 0.6749999999999996, + 0.6999999999999995, + 0.7249999999999994, + 0.7499999999999993, + 0.7749999999999992, + 0.7999999999999992, + 0.8249999999999991, + 0.849999999999999, + 0.8749999999999989, + 0.8999999999999988, + 0.9249999999999987, + 0.9499999999999986, + 0.9749999999999985, + 0.9999999999999984 + ], + [ + -65.0, + -57.61698001518964, + -50.350680331662666, + -43.190959064808595, + -36.118349378635024, + -36.45214555816893, + -36.64013375770745, + -36.63901228066678, + -36.40785016818501, + -35.90739311979073, + -35.098342787028955, + -33.938983603152764, + -32.38249620775813, + -30.37433493239779, + -27.85023024839231, + -24.735811936721902, + -20.949673938481457, + -16.41300375864941, + -11.070338662286476, + -4.925845486416181, + 1.9065110859235475, + 9.156953785515793, + 16.396750192384502, + 23.114467243140304, + 28.85862394448292, + 33.36981164763734, + 36.623187926589694, + 38.77284096573438, + 40.05395516480963, + 40.70121687913082, + 40.90634885315981, + 40.808208123873015, + 40.499949593417945, + 40.04123645048496, + 39.469454750133934, + 38.808051820286906, + 38.07208641230601, + 37.271686168441654, + 36.414113931544115, + 35.50497068675557, + 34.54888423211685 + ] ] -} \ No newline at end of file +} diff --git a/test/hoctests/tests/test_kschan.py b/test/hoctests/tests/test_kschan.py index 3774b37e56..3c5987c7a1 100644 --- a/test/hoctests/tests/test_kschan.py +++ b/test/hoctests/tests/test_kschan.py @@ -37,7 +37,7 @@ def chkpr(key): h.load_file("chanbild.hoc") # For checking if a run is doing something useful -# Activate the graphics by uncomment the "# return" statement in +# To activate graphics, comment the "return" statement in # hrun below. Need to press the "Continue" button after each pause. # If the graph seems incorrect or unexpected, you can stop by hitting # (ctrl)C in the terminal window and then pressing the "Continue" button @@ -58,17 +58,21 @@ def hrun(name, t_tol=0.0, v_tol=0.0, v_tol_per_time=0.0): The reference files are typically generated with GCC, and the tolerances are typically driven by the requirements of NVHPC and oneAPI... """ + print(name) h.run() ref_data = chk.get(name) + new_tv, new_vv = trec.to_python(), vrec.to_python() if ref_data is None: - raise Exception("No reference data for key: " + key) + chk("ZZZ" + name, [new_tv, new_vv]) + chk.save() + raise Exception("No reference data for key: " + name) ref_tv, ref_vv = ref_data - new_tv, new_vv = trec.to_python(), vrec.to_python() assert len(ref_tv) == len(ref_vv) assert len(ref_tv) == len(new_tv) assert len(ref_tv) == len(new_vv) match = True max_diff_t, max_diff_v = 0.0, 0.0 + # Interpolate the new v values to the reference t values def interp(new_t, old_t, old_v): assert np.all(np.diff(old_t) > 0) @@ -124,12 +128,17 @@ def test_1(): s, ic = cell() s.insert("khh") # exists in soma and has one state chkstdout("khh inserted", capture_stdout("h.psection()", True)) - # It is not supported (anymore) to change the number of variables + # It is not supported (anymore) to change the number or names of variables # of a mechanism while instances of that mechanism are active. + # This is because name and number changes re-register the mechanism. # In this case the change would be from 1 state to 2 states. expect_err("cb.nahh()") # cb changes name and inserted na_ion before failure - cb.ks.name("khh") # change name back - chkstdout("khh same except for na_ion", capture_stdout("h.psection()", True)) + # change name back (but there is still an instance) + expect_err('cb.ks.name("khh");1/0') + s.uninsert("khh") + cb.ks.name("khh") + s.insert("khh") + chkstdout("khh same", capture_stdout("h.psection()", True)) s.uninsert("khh") cb.nahh() # try again s.insert("nahh") diff --git a/test/hoctests/tests/test_legacy_setpointer.py b/test/hoctests/tests/test_legacy_setpointer.py new file mode 100644 index 0000000000..755d33306d --- /dev/null +++ b/test/hoctests/tests/test_legacy_setpointer.py @@ -0,0 +1,36 @@ +# h.setpointer is deprecated in favor of h._ref_foo = h.ref_bar +# This test file started out life to cover nrnpy_setpointer_helper + +from neuron import h +from neuron.expect_hocerr import expect_err, set_quiet + +set_quiet(False) + + +def test_setpointer1(): + s = h.Section("soma") + s.insert("sdatats") + seg = s(0.5) + mech = seg.sdatats + vref = seg._ref_v + h.setpointer(vref, "p", mech) + seg.v = -10 + print(seg.v, mech.p, seg, mech) + mech.p = 25 + print(seg.v, mech.p, seg, mech) + return s + + +def test_setpointer2(): + s = h.Section("soma") + s.insert("sdatats") + seg = s(0.5) + + expect_err('h.setpointer(seg._ref_v, "foo", seg.sdatats)') + expect_err('h.setpointer(seg._ref_v, "p", seg)') + expect_err("h.setpointer(seg._ref_v, 5, seg.sdatats)") + + +if __name__ == "__main__": + s = test_setpointer1() + s = test_setpointer2() diff --git a/test/hoctests/tests/test_mechstd.py b/test/hoctests/tests/test_mechstd.py new file mode 100644 index 0000000000..990e232baa --- /dev/null +++ b/test/hoctests/tests/test_mechstd.py @@ -0,0 +1,180 @@ +from neuron import h, gui +from neuron.expect_hocerr import expect_err, set_quiet +import math + +set_quiet(False) + + +def mkmodel(mnames): + s = h.Section(name="dend") + for name in mnames: + if name != "capacitance": + s.insert(name) + return s + + +# for MechStan, mechname, varname, isarray, aindex var_ref_in_seg in variter(mslist, seg) +def variter(mslist, seg=None): + for m in mslist: + mname = h.ref("") + m.name(mname) + for i in range(m.count()): + varname = h.ref("") + size = m.name(varname, i) + isarray = m.is_array(i) + refname = "_ref_" + varname[0] + ref = getattr(seg, "_ref_" + varname[0]) if seg else None + ref = ref[0] if seg and isarray else ref + for aindex in range(size): + yield m, mname[0], varname[0], isarray, aindex, ref + + +def test_mechstd1(): # default params + def mechstd(vartype, mnames): + # print("\nvartype ", vartype) + ms = [h.MechanismStandard(name, vartype) for name in mnames] + s = h.Section() + for name in mnames: + if name != "capacitance": + s.insert(name) + + for m in ms: + name = h.ref("") + m.name(name) + for i in range(m.count()): + varname = h.ref("") + size = m.name(varname, i) + if not m.is_array(i): + # print(name[0], i, varname[0], size, m.get(varname[0])) + assert getattr(s(0.5), varname[0]) == m.get(varname[0]) + else: + for j in range(size): + # print(name[0], i, varname[0], j, size, m.get(varname[0])) + assert getattr(s(0.5), varname[0])[j] == m.get(varname[0], j) + + mnames = ["hh", "pas", "fastpas", "capacitance", "na_ion", "extracellular"] + for vartype in [1, 2, 3, 0]: + mechstd(vartype, mnames) + for nlayer in [3, 1, 2]: + h.nlayer_extracellular(nlayer) + mechstd(0, ["extracellular"]) + + +def test_mechstd2(): # test set, get, in, out + mnames = ["hh", "pas", "fastpas", "capacitance", "na_ion", "extracellular"] + mslist = [h.MechanismStandard(name, 0) for name in mnames] + # fill with distinct values + val = 0.0 + for ms, mname, varname, isarray, index, ref in variter(mslist, None): + val += 0.1 + ms.set(varname, val, index) + + val = 0.0 # test ms.get + for ms, mname, varname, isarray, index, ref in variter(mslist, None): + val += 0.1 + assert math.isclose(ms.get(varname, index), val) + + # apply mslist values into model and test that they got there + + model = mkmodel(mnames) + for ms in mslist: + ms.out(model(0.5)) + for ms, mname, varname, isarray, index, ref in variter(mslist, model(0.5)): + assert math.isclose(ms.get(varname, index), ref[index]) + # since they got out, see if they can get back in with new mslist + mslist = [h.MechanismStandard(name, 0) for name in mnames] + for ms in mslist: + ms._in(model(0.5)) + for ms, mname, varname, isarray, index, ref in variter(mslist, model(0.5)): + # print(ms, mname, varname, ms.get(varname, index), isarray, index, ref) + assert math.isclose(ms.get(varname, index), ref[index]) + + +def test_mechstd3(): + h("""x=0""") + expect_err('ms = h.MechanismStandard("x")') # cover constructor + + +def test_mechstd4(): # HOC mechanism + h( + """ +begintemplate Foo +public x +x = 1 +proc init() { + x = 5 +} +endtemplate Foo +""" + ) + h.make_mechanism("foo", "Foo", "x") + + ms = h.MechanismStandard("foo") + print(ms.get("x_foo")) + + print("calling mkmodel") + s = mkmodel(["foo"]) + print("return from mkmodel") + print(s(0.5).x_foo) + ms._in(s(0.5)) + print(ms.get("x_foo")) + ms.set("x_foo", 6) + ms.out(s(0.5)) + print(s(0.5).foo.x) + ms2 = h.MechanismStandard("foo") + ms2._in(ms) + assert ms2.get("x_foo") == ms.get("x_foo") + ms2.set("x_foo", 7) + ms2.out(ms) + assert ms.get("x_foo") == 7.0 + + +def mechact(ms, i, index): + print(ms, i, index) + + +def sv(b, ms): + b.save("// hello from sv(%s, %s) " % (str(b), str(ms))) + ms.save("name") + + +def test_mechstd5(): + model = mkmodel(["sdata"]) + ms = h.MechanismStandard("sdata") + b = h.HBox() + b.save((sv, (b, ms))) + b.intercept(1) + ms.panel() + ms.action(mechact) + ms.panel("label name") + b.intercept(0) + b.map() + + pp = h.SData(model(0.5)) + pp.a = 5 + ms2 = h.MechanismStandard("SData") + print(pp.a) + ms2._in(pp) + print(ms2.get("a")) + h.save_session("tmp.ses") + b.unmap() + + +def test_mechstd6(): + # Since SymChooser.run() is a dialog, to cover load_mechanism, must + # manually navigate to sdata and SData[0] + # Note SymDirectoryImpl::load_mechanism is covered by + # test/cover/unit_tests/cover.cpp + h("create soma") + h.soma.insert("sdata") + pp = h.SData(h.soma(0.5)) + sc = h.SymChooser() + sc.run() + + +if __name__ == "__main__": + test_mechstd1() + test_mechstd2() + test_mechstd3() + test_mechstd4() + test_mechstd5() diff --git a/test/pytest_coreneuron/test_partrans.py b/test/pytest_coreneuron/test_partrans.py index b4aa11e136..4df330a29c 100644 --- a/test/pytest_coreneuron/test_partrans.py +++ b/test/pytest_coreneuron/test_partrans.py @@ -59,6 +59,7 @@ def expect_error(callable, args, sec=None): ks.gmax(0) ks.erev(0) + # Cell with enough nonsense stuff to exercise transfer possibilities. class Cell: def __init__(self): @@ -186,7 +187,6 @@ def check_values(): def test_partrans(): - # no transfer targets or sources. mkmodel(4) run() @@ -311,6 +311,9 @@ def test_partrans(): pc.target_var(cell.hgap[0], cell.hgap[0]._ref_e, 1000) pc.setup_transfer() imp = h.Impedance() + for z in h.List("HGap"): + z.gmax = 1.0 # imp.compute converges slowly + del z h.finitialize(-65) if pc.gid_exists(0): imp.loc(pc.gid2cell(0).soma(0.5)) From 8afc9decf8650b4f5197f58c2535590d8c21bc31 Mon Sep 17 00:00:00 2001 From: Pramod Kumbhar Date: Wed, 6 Mar 2024 07:13:28 +0100 Subject: [PATCH 24/26] Bug fix: avoid coreneuron file mode to not use in-memory model copy (#2767) - @WeinaJi pointed out that when file mode is set to True, coreneuron was still using "in memory mode" from the neuron side. - This is because "file mode" is also part of coreneuron "embedded mode" and we didn't distinguish file mode vs in-memory mode in embedded node. - As part of this PR, we simply pass the file mode flag to core neuron so that we can decide if we should read the model from the file or memory. --- src/coreneuron/io/nrn_setup.cpp | 11 +++-- src/coreneuron/mechanism/mech/enginemech.cpp | 7 ++- src/nrniv/nrncore_write.cpp | 48 +++++++++++--------- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/coreneuron/io/nrn_setup.cpp b/src/coreneuron/io/nrn_setup.cpp index 8697a9015d..0a3dc11e87 100644 --- a/src/coreneuron/io/nrn_setup.cpp +++ b/src/coreneuron/io/nrn_setup.cpp @@ -44,6 +44,7 @@ /// --> Coreneuron bool corenrn_embedded; +bool corenrn_file_mode; int corenrn_embedded_nthread; void (*nrn2core_group_ids_)(int*); @@ -170,7 +171,7 @@ std::vector> nrnthreads_netcon_negsrcgid_tid; /* read files.dat file and distribute cellgroups to all mpi ranks */ void nrn_read_filesdat(int& ngrp, int*& grp, const char* filesdat) { patstimtype = nrn_get_mechtype("PatternStim"); - if (corenrn_embedded) { + if (corenrn_embedded && !corenrn_file_mode) { ngrp = corenrn_embedded_nthread; grp = new int[ngrp + 1]; (*nrn2core_group_ids_)(grp); @@ -473,8 +474,8 @@ void nrn_setup(const char* filesdat, // of phase2. So gap junction setup is deferred to after phase2. nrnthreads_netcon_negsrcgid_tid.resize(nrn_nthread); - if (!corenrn_embedded) { - coreneuron::phase_wrapper(userParams); + if (corenrn_file_mode) { + coreneuron::phase_wrapper(userParams, !corenrn_file_mode); } else { nrn_multithread_job([](NrnThread* n) { Phase1 p1{n->id}; @@ -490,7 +491,7 @@ void nrn_setup(const char* filesdat, // read the rest of the gidgroup's data and complete the setup for each // thread. /* nrn_multithread_job supports serial, pthread, and openmp. */ - coreneuron::phase_wrapper(userParams, corenrn_embedded); + coreneuron::phase_wrapper(userParams, !corenrn_file_mode); // gap junctions // Gaps are done after phase2, in order to use layout and permutation @@ -917,7 +918,7 @@ void read_phase1(NrnThread& nt, UserParams& userParams) { void read_phase2(NrnThread& nt, UserParams& userParams) { Phase2 p2; - if (corenrn_embedded) { + if (corenrn_embedded && !corenrn_file_mode) { p2.read_direct(nt.id, nt); } else { p2.read_file(userParams.file_reader[nt.id], nt); diff --git a/src/coreneuron/mechanism/mech/enginemech.cpp b/src/coreneuron/mechanism/mech/enginemech.cpp index 19549228ba..d7ef974731 100644 --- a/src/coreneuron/mechanism/mech/enginemech.cpp +++ b/src/coreneuron/mechanism/mech/enginemech.cpp @@ -45,6 +45,7 @@ extern "C" { /// global variables from coreneuron library extern bool corenrn_embedded; +extern bool corenrn_file_mode; extern int corenrn_embedded_nthread; /// parse arguments from neuron and prepare new one for coreneuron @@ -107,7 +108,8 @@ int corenrn_embedded_run(int nthread, int use_mpi, int use_fast_imem, const char* mpi_lib, - const char* nrn_arg) { + const char* nrn_arg, + int file_mode) { bool corenrn_skip_write_model_to_disk = false; const std::string corenrn_skip_write_model_to_disk_arg{"--skip-write-model-to-disk"}; // If "only_simulate_str" exists in "nrn_arg" then avoid transferring any data between NEURON @@ -123,6 +125,7 @@ int corenrn_embedded_run(int nthread, } // set coreneuron's internal variable based on neuron arguments corenrn_embedded = !corenrn_skip_write_model_to_disk; + corenrn_file_mode = file_mode; corenrn_embedded_nthread = nthread; coreneuron::nrn_have_gaps = have_gaps != 0; coreneuron::nrn_use_fast_imem = use_fast_imem != 0; @@ -181,6 +184,8 @@ int solve_core(int argc, char** argv) { args.append(" "); } + corenrn_file_mode = true; + // add mpi library argument add_mpi_library_arg("", args); diff --git a/src/nrniv/nrncore_write.cpp b/src/nrniv/nrncore_write.cpp index cd9550f0b7..29d6bdc978 100644 --- a/src/nrniv/nrncore_write.cpp +++ b/src/nrniv/nrncore_write.cpp @@ -270,6 +270,25 @@ static void part2(const char* path) { #if defined(HAVE_DLFCN_H) + +/** Return neuron.coreneuron.enable */ +int nrncore_is_enabled() { + if (nrnpy_nrncore_enable_value_p_) { + int result = (*nrnpy_nrncore_enable_value_p_)(); + return result; + } + return 0; +} + +/** Return value of neuron.coreneuron.file_mode flag */ +int nrncore_is_file_mode() { + if (nrnpy_nrncore_file_mode_value_p_) { + int result = (*nrnpy_nrncore_file_mode_value_p_)(); + return result; + } + return 0; +} + /** Launch CoreNEURON in direct memory mode */ int nrncore_run(const char* arg) { // using direct memory mode @@ -305,7 +324,7 @@ int nrncore_run(const char* arg) { map_coreneuron_callbacks(handle); // lookup symbol from coreneuron for launching - using launcher_t = int (*)(int, int, int, int, const char*, const char*); + using launcher_t = int (*)(int, int, int, int, const char*, const char*, int); auto* const coreneuron_launcher = reinterpret_cast( dlsym(handle, "corenrn_embedded_run")); if (!coreneuron_launcher) { @@ -333,8 +352,13 @@ int nrncore_run(const char* arg) { #endif // launch coreneuron - int result = coreneuron_launcher( - nrn_nthread, have_gap, nrnmpi_use, nrn_use_fast_imem, corenrn_mpi_library.c_str(), arg); + int result = coreneuron_launcher(nrn_nthread, + have_gap, + nrnmpi_use, + nrn_use_fast_imem, + corenrn_mpi_library.c_str(), + arg, + nrncore_is_file_mode()); // close handle and return result dlclose(handle); @@ -353,24 +377,6 @@ int nrncore_run(const char* arg) { return result; } -/** Return neuron.coreneuron.enable */ -int nrncore_is_enabled() { - if (nrnpy_nrncore_enable_value_p_) { - int b = (*nrnpy_nrncore_enable_value_p_)(); - return b; - } - return 0; -} - -/** Return value of neuron.coreneuron.file_mode flag */ -int nrncore_is_file_mode() { - if (nrnpy_nrncore_file_mode_value_p_) { - int result = (*nrnpy_nrncore_file_mode_value_p_)(); - return result; - } - return 0; -} - /** Find folder set for --datpath CLI option in CoreNEURON to dump the CoreNEURON data * Note that it is expected to have the CLI option passed in the form of `--datpath ` * All the logic to find the proper folder to dump the coreneuron files in file_mode is From e3e8625d085b42be68127b5e1295174a8e8dee44 Mon Sep 17 00:00:00 2001 From: nrnhines Date: Thu, 7 Mar 2024 10:07:36 -0500 Subject: [PATCH 25/26] More or less revert #2729 but print full path for mod files. (#2764) * Use PWD only in compile and printf lines --- bin/nrnivmodl.in | 12 ++++++++---- bin/nrnivmodl_makefile_cmake.in | 14 +++++++------- test/CMakeLists.txt | 6 ++++++ test/nmodl/Project Path Space/test.mod | 6 ++++++ test/nmodl/Project Path Space/test.py | 9 +++++++++ 5 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 test/nmodl/Project Path Space/test.mod create mode 100644 test/nmodl/Project Path Space/test.py diff --git a/bin/nrnivmodl.in b/bin/nrnivmodl.in index 7cc200a6e4..3df1ad6e4e 100755 --- a/bin/nrnivmodl.in +++ b/bin/nrnivmodl.in @@ -157,6 +157,10 @@ mdir="$PWD" # construct file to be included by makefile to specify mod to c rule when # executed in $MODSUBDIR (child folder of launch location folder) +# Since $(PWD) may contain spaces in the path name, use the relative path. +# Otherwise, it is very difficult to pass around paths with spaces in +# makefiles. However, to help the user know where things are, when +# compiling use the full path to *.cpp MODMAKE=makemod2c_inc > "$MODMAKE" for i in "${files[@]}" ; do @@ -170,13 +174,13 @@ for i in "${files[@]}" ; do f=${f// /\\ } f=${f//:/\\:} echo "\ -\$(PWD)/${base_name// /\\ }.cpp: ${f}.mod \$(NOCMODL) +${base_name// /\\ }.cpp: ${f}.mod \$(NOCMODL) @printf \" -> \$(C_GREEN)NMODL\$(C_RESET) \$<\\\n\" (cd \"$dir_name\"; @NRN_NOCMODL_SANITIZER_ENVIRONMENT_STRING@ MODLUNIT=\$(NRNUNITS) \$(NOCMODL) \"$base_name.mod\" -o \"$mdir\" $UserNMODLFLAGS) -./${base_name// /\\ }.o: \$(PWD)/${base_name// /\\ }.cpp - @printf \" -> \$(C_GREEN)Compiling\$(C_RESET) \$<\\\n\" - \$(CXXCOMPILE) -I\"$dir_name\" \$(INCLUDES) @CMAKE_CXX_COMPILE_OPTIONS_PIC@ -c \$< -o \$@ +./${base_name// /\\ }.o: ${base_name// /\\ }.cpp + @printf \" -> \$(C_GREEN)Compiling\$(C_RESET) ${PWD}/\$<\\\n\" + \$(CXXCOMPILE) -I\"$dir_name\" \$(INCLUDES) @CMAKE_CXX_COMPILE_OPTIONS_PIC@ -c \"${PWD}/\$<\" -o \$@ " >> "$MODMAKE" done diff --git a/bin/nrnivmodl_makefile_cmake.in b/bin/nrnivmodl_makefile_cmake.in index 4894ac4c72..71eade96ae 100644 --- a/bin/nrnivmodl_makefile_cmake.in +++ b/bin/nrnivmodl_makefile_cmake.in @@ -11,9 +11,9 @@ # Mechanisms version are by default 0.0, but should be overriden MECH_NAME = MECH_VERSION = 0.0 -MODS_PATH = $(PWD) +MODS_PATH = . # in the @CMAKE_HOST_SYSTEM_PROCESSOR@ folder -OUTPUT = $(PWD) +OUTPUT = . DESTDIR = UserINCFLAGS = UserLDFLAGS = @@ -78,8 +78,8 @@ endif NRNUNITS = $(datadir_lib)/nrnunits.lib # File path config (internal) -MODC_DIR = $(PWD) -OBJS_DIR = $(PWD) +MODC_DIR = . +OBJS_DIR = . mod_objs = $(MODOBJFILES) mod_func_o = $(OBJS_DIR)/mod_func.o @@ -122,20 +122,20 @@ C_GREEN := \033[32m # Take the main and link with nrnmech. # RPATH is set for DESTDIR_RPATH and coreneuron lib special: $(mech_lib) - @printf " => $(C_GREEN)LINKING$(C_RESET) executable $(special) LDFLAGS are: $(LDFLAGS)\n" + @printf " => $(C_GREEN)LINKING$(C_RESET) executable \"${PWD}/$(special)\" LDFLAGS are: $(LDFLAGS)\n" $(CXX_LINK_EXE) -I $(incdir) -I $(incdir)/nrncvode -DAUTO_DLOPEN_NRNMECH=0 $(datadir)/nrnmain.cpp -o $(special) \ -L$(OBJS_DIR) -l$(mech_libname) $(NRNLIB_FLAGS) -l$(mech_libname) $(extra_lib_link) -Wl,-rpath,'$(DESTDIR_RPATH)' -Wl,-rpath,$(libdir) $(LDFLAGS) $(EXTRA_LDFLAGS) $(mech_lib): $(mech_lib_type) mech_lib_shared: mod_func.o $(mod_objs) build_always - @printf " => $(C_GREEN)LINKING$(C_RESET) shared library $(mech_lib)\n" + @printf " => $(C_GREEN)LINKING$(C_RESET) shared library \"${PWD}/$(mech_lib)\"\n" $(CXX_LINK_SHARED) -I $(incdir) -o ${mech_lib} ${_SONAME} \ $(mod_func_o) $(mod_objs) $(NRNLIB_FLAGS) $(NRNLIB_RPATH_FLAGS) $(LDFLAGS) rm -f $(OBJS_DIR)/.libs/libnrnmech.so ; mkdir -p $(OBJS_DIR)/.libs ; cp $(mech_lib) $(OBJS_DIR)/.libs/libnrnmech.so mech_lib_static: mod_func.o $(mod_objs) build_always - @printf " => $(C_GREEN)LINKING$(C_RESET) static library $(mech_lib)\n" + @printf " => $(C_GREEN)LINKING$(C_RESET) static library \"${PWD}/$(mech_lib)\"\n" ar cq ${mech_lib} $(mod_func_o) $(mod_objs) $(cobjs); mod_func.o: mod_func.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bb07cc86d7..d696d3b76a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -511,6 +511,12 @@ if(NRN_ENABLE_PYTHON) SCRIPT_PATTERNS test/nmodl/test_random.py ENVIRONMENT ${sonata_zero_gid_env} ${nrnpython_mpi_env} COMMAND ${modtests_launch_py} test/nmodl/test_random.py) + nrn_add_test( + GROUP nmodl_tests + NAME "test_path_spaces" + SCRIPT_PATTERNS "test/nmodl/Project Path Space/test.*" + ENVIRONMENT ${sonata_zero_gid_env} ${nrnpython_mpi_env} + COMMAND bash -c "pwd && cd 'test/nmodl/Project Path Space' && nrnivmodl") nrn_add_test_group( CORENEURON diff --git a/test/nmodl/Project Path Space/test.mod b/test/nmodl/Project Path Space/test.mod new file mode 100644 index 0000000000..d96827341a --- /dev/null +++ b/test/nmodl/Project Path Space/test.mod @@ -0,0 +1,6 @@ +NEURON {SUFFIX nothing} + +FUNCTION foo() { + foo = 3 +} + diff --git a/test/nmodl/Project Path Space/test.py b/test/nmodl/Project Path Space/test.py new file mode 100644 index 0000000000..c373e275ad --- /dev/null +++ b/test/nmodl/Project Path Space/test.py @@ -0,0 +1,9 @@ +from neuron import h + + +def test_space(): + assert h.foo() == 3.0 + + +if __name__ == "__main__": + test_space() From 17be061e031701736cb03a5088b7ddf253a069a4 Mon Sep 17 00:00:00 2001 From: Weina Ji Date: Wed, 13 Mar 2024 21:08:01 +0100 Subject: [PATCH 26/26] Callbacks for reading mapping information from memory (#2555) * Implement necessary changes for in-memory transfer for `gid_3.dat` i.e. report related information * no need to send ReportEvent back to nrn, skip them * add new test from ringtests --- src/coreneuron/io/core2nrn_data_return.cpp | 4 + src/coreneuron/io/nrn2core_direct.h | 15 +++ src/coreneuron/io/nrn_filehandler.hpp | 13 +-- src/coreneuron/io/nrn_setup.cpp | 38 ++----- src/coreneuron/io/phase3.cpp | 101 ++++++++++++++++++ src/coreneuron/io/phase3.hpp | 21 ++++ src/coreneuron/io/reports/report_event.hpp | 3 + src/coreneuron/network/netcon.hpp | 1 + .../callbacks/nrncore_callbacks.cpp | 36 +++++++ .../callbacks/nrncore_callbacks.h | 15 +++ src/nrniv/nrncore_write/io/nrncore_io.cpp | 59 +++++++--- test/external/CMakeLists.txt | 2 +- test/external/ringtest/CMakeLists.txt | 39 +++++++ 13 files changed, 292 insertions(+), 55 deletions(-) create mode 100644 src/coreneuron/io/phase3.cpp create mode 100644 src/coreneuron/io/phase3.hpp diff --git a/src/coreneuron/io/core2nrn_data_return.cpp b/src/coreneuron/io/core2nrn_data_return.cpp index 5b13d4880d..3aa230911a 100644 --- a/src/coreneuron/io/core2nrn_data_return.cpp +++ b/src/coreneuron/io/core2nrn_data_return.cpp @@ -572,6 +572,10 @@ static bool core2nrn_tqueue_item(TQItem* q, SelfEventWeightMap& sewm, NrnThread& // nothing to transfer break; } + case ReportEventType: { + // no need to transfer ReportEvent + break; + } default: { // In particular, InputPreSyn does not appear in tqueue as it // immediately fans out to NetCon. diff --git a/src/coreneuron/io/nrn2core_direct.h b/src/coreneuron/io/nrn2core_direct.h index 5790f2197d..2c81fda5a7 100644 --- a/src/coreneuron/io/nrn2core_direct.h +++ b/src/coreneuron/io/nrn2core_direct.h @@ -10,6 +10,7 @@ #include #include +#include extern "C" { // The callbacks into nrn/src/nrniv/nrnbbcore_write.cpp to get @@ -96,6 +97,20 @@ extern int (*nrn2core_get_dat2_vecplay_inst_)(int tid, extern void (*nrn2core_part2_clean_)(); +extern void (*nrn2core_get_dat3_cell_count_)(int& cell_count); +extern void ( + *nrn2core_get_dat3_cellmapping_)(int i, int& gid, int& nsec, int& nseg, int& n_seclist); +extern void (*nrn2core_get_dat3_secmapping_)(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp); + /* what variables to send back to NEURON on each time step */ extern void (*nrn2core_get_trajectory_requests_)(int tid, int& bsize, diff --git a/src/coreneuron/io/nrn_filehandler.hpp b/src/coreneuron/io/nrn_filehandler.hpp index 9ac7f048e8..3241fd3506 100644 --- a/src/coreneuron/io/nrn_filehandler.hpp +++ b/src/coreneuron/io/nrn_filehandler.hpp @@ -127,16 +127,13 @@ class FileHandler { mapinfo->name = std::string(name); if (nseg) { - std::vector sec, seg; - std::vector lfp_factors; - - sec.reserve(nseg); - seg.reserve(nseg); - lfp_factors.reserve(total_lfp_factors); + auto sec = read_vector(nseg); + auto seg = read_vector(nseg); - read_array(&sec[0], nseg); - read_array(&seg[0], nseg); + std::vector lfp_factors; if (total_lfp_factors > 0) { + // ASan reports container overflow on read_array with vec.reserve, resize does work + lfp_factors.resize(nseg); read_array(&lfp_factors[0], total_lfp_factors); } diff --git a/src/coreneuron/io/nrn_setup.cpp b/src/coreneuron/io/nrn_setup.cpp index 0a3dc11e87..888f07949a 100644 --- a/src/coreneuron/io/nrn_setup.cpp +++ b/src/coreneuron/io/nrn_setup.cpp @@ -34,6 +34,7 @@ #include "coreneuron/utils/nrnoc_aux.hpp" #include "coreneuron/io/phase1.hpp" #include "coreneuron/io/phase2.hpp" +#include "coreneuron/io/phase3.hpp" #include "coreneuron/io/mech_report.h" #include "coreneuron/io/reports/nrnreport.hpp" @@ -516,7 +517,7 @@ void nrn_setup(const char* filesdat, } if (is_mapping_needed) - coreneuron::phase_wrapper(userParams); + coreneuron::phase_wrapper(userParams, !corenrn_file_mode); *mindelay = set_mindelay(*mindelay); @@ -928,37 +929,16 @@ void read_phase2(NrnThread& nt, UserParams& userParams) { /** read mapping information for neurons */ void read_phase3(NrnThread& nt, UserParams& userParams) { - /** restore checkpoint state (before restoring queue items */ - auto& F = userParams.file_reader[nt.id]; - F.restore_checkpoint(); - /** mapping information for all neurons in single NrnThread */ NrnThreadMappingInfo* ntmapping = new NrnThreadMappingInfo(); - int count = 0; - - F.read_mapping_cell_count(&count); - - /** number of cells in mapping file should equal to cells in NrnThread */ - nrn_assert(count == nt.ncell); - - /** for every neuron */ - for (int i = 0; i < nt.ncell; i++) { - int gid, nsec, nseg, nseclist; - - // read counts - F.read_mapping_count(&gid, &nsec, &nseg, &nseclist); - - CellMapping* cmap = new CellMapping(gid); - - // read section-segment mapping for every section list - for (int j = 0; j < nseclist; j++) { - SecMapping* smap = new SecMapping(); - F.read_mapping_info(smap, ntmapping, cmap); - cmap->add_sec_map(smap); - } - - ntmapping->add_cell_mapping(cmap); + Phase3 p3; + if (corenrn_embedded && !corenrn_file_mode) { + p3.read_direct(ntmapping); + } else { + auto& F = userParams.file_reader[nt.id]; + F.restore_checkpoint(); + p3.read_file(F, ntmapping); } // make number #cells match with mapping size diff --git a/src/coreneuron/io/phase3.cpp b/src/coreneuron/io/phase3.cpp new file mode 100644 index 0000000000..ca73ac829c --- /dev/null +++ b/src/coreneuron/io/phase3.cpp @@ -0,0 +1,101 @@ +/* +# ============================================================================= +# Copyright (c) 2016 - 2024 Blue Brain Project/EPFL +# +# See top-level LICENSE file for details. +# ============================================================================= +*/ +#include + +#include "coreneuron/io/phase3.hpp" +// Where nrn2core_get_dat3_secmapping_ is declared with extern "C" to avoid symbol name mangling +// caused by dual ABI for std::string +#include "coreneuron/io/nrn2core_direct.h" + +void (*nrn2core_get_dat3_cell_count_)(int& cell_count); +void (*nrn2core_get_dat3_cellmapping_)(int i, int& gid, int& nsec, int& nseg, int& n_seclist); +void (*nrn2core_get_dat3_secmapping_)(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp); + +namespace coreneuron { +void Phase3::read_file(FileHandler& F, NrnThreadMappingInfo* ntmapping) { + int count = 0; + F.read_mapping_cell_count(&count); + /** for every neuron */ + for (int i = 0; i < count; i++) { + int gid, nsec, nseg, nseclist; + // read counts + F.read_mapping_count(&gid, &nsec, &nseg, &nseclist); + CellMapping* cmap = new CellMapping(gid); + // read section-segment mapping for every section list + for (int j = 0; j < nseclist; j++) { + SecMapping* smap = new SecMapping(); + F.read_mapping_info(smap, ntmapping, cmap); + cmap->add_sec_map(smap); + } + ntmapping->add_cell_mapping(cmap); + } +} + +void Phase3::read_direct(NrnThreadMappingInfo* ntmapping) { + int count; + nrn2core_get_dat3_cell_count_(count); + /** for every neuron */ + for (int i = 0; i < count; i++) { + int gid; + int t_sec; + int t_seg; + int nseclist; + nrn2core_get_dat3_cellmapping_(i, gid, t_sec, t_seg, nseclist); + auto cmap = new CellMapping(gid); + for (int j = 0; j < nseclist; j++) { + std::string sclname; + int n_sec; + int n_seg; + int n_electrodes; + size_t total_lfp_factors; + std::vector data_sec; + std::vector data_seg; + std::vector data_lfp; + nrn2core_get_dat3_secmapping_(i, + j, + sclname, + n_sec, + n_seg, + total_lfp_factors, + n_electrodes, + data_sec, + data_seg, + data_lfp); + auto smap = new SecMapping(); + smap->name = sclname; + for (int i_seg = 0; i_seg < n_seg; i_seg++) { + smap->add_segment(data_sec[i_seg], data_seg[i_seg]); + ntmapping->add_segment_id(data_seg[i_seg]); + int factor_offset = i_seg * n_electrodes; + if (total_lfp_factors > 0) { + // Abort if the factors contains a NaN + nrn_assert(count_if(data_lfp.begin(), data_lfp.end(), [](double d) { + return std::isnan(d); + }) == 0); + std::vector segment_factors(data_lfp.begin() + factor_offset, + data_lfp.begin() + factor_offset + + n_electrodes); + cmap->add_segment_lfp_factor(data_seg[i], segment_factors); + } + } + cmap->add_sec_map(smap); + } + ntmapping->add_cell_mapping(cmap); + } +} + +} // namespace coreneuron diff --git a/src/coreneuron/io/phase3.hpp b/src/coreneuron/io/phase3.hpp new file mode 100644 index 0000000000..eae88a437a --- /dev/null +++ b/src/coreneuron/io/phase3.hpp @@ -0,0 +1,21 @@ +/* +# ============================================================================= +# Copyright (c) 2016 - 2024 Blue Brain Project/EPFL +# +# See top-level LICENSE file for details. +# ============================================================================= +*/ + +#pragma once + +#include "coreneuron/io/nrn_filehandler.hpp" + +namespace coreneuron { +struct NrnThreadMappingInfo; + +class Phase3 { + public: + void read_file(FileHandler& F, NrnThreadMappingInfo* ntmapping); + void read_direct(NrnThreadMappingInfo* ntmapping); +}; +} // namespace coreneuron diff --git a/src/coreneuron/io/reports/report_event.hpp b/src/coreneuron/io/reports/report_event.hpp index 0bb9a922b2..e019fe6582 100644 --- a/src/coreneuron/io/reports/report_event.hpp +++ b/src/coreneuron/io/reports/report_event.hpp @@ -44,6 +44,9 @@ class ReportEvent: public DiscreteEvent { bool require_checkpoint() override; void summation_alu(NrnThread* nt); void lfp_calc(NrnThread* nt); + int type() const override { + return ReportEventType; + } private: double dt; diff --git a/src/coreneuron/network/netcon.hpp b/src/coreneuron/network/netcon.hpp index 613a454f43..fc1ee75aab 100644 --- a/src/coreneuron/network/netcon.hpp +++ b/src/coreneuron/network/netcon.hpp @@ -26,6 +26,7 @@ class NetCvode; #define PreSynType 4 #define NetParEventType 7 #define InputPreSynType 20 +#define ReportEventType 8 struct DiscreteEvent { DiscreteEvent() = default; diff --git a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp index ec88e782a7..124e2d071d 100644 --- a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp +++ b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.cpp @@ -18,6 +18,7 @@ extern TQueue* net_cvode_instance_event_queue(NrnThread*); #include "vrecitem.h" // for nrnbbcore_vecplay_write #include "nrnwrap_dlfcn.h" +#include "nrnsection_mapping.h" extern bbcore_write_t* nrn_bbcore_write_; extern bbcore_write_t* nrn_bbcore_read_; @@ -26,6 +27,7 @@ extern bool corenrn_direct; extern int* bbcore_dparam_size; extern double nrn_ion_charge(Symbol*); extern CellGroup* cellgroups_; +extern NrnMappingInfo mapinfo; extern NetCvode* net_cvode_instance; extern char* pnt_map; extern void* nrn_interthread_enqueue(NrnThread*); @@ -219,6 +221,40 @@ int nrnthread_dat1(int tid, return 1; } +void nrnthread_dat3_cell_count(int& cell_count) { + cell_count = mapinfo.size(); +} + +void nrnthread_dat3_cellmapping(int i, int& gid, int& nsec, int& nseg, int& n_seclist) { + CellMapping* c = mapinfo.mapping[i]; + gid = c->gid; + nsec = c->num_sections(); + nseg = c->num_segments(); + n_seclist = c->size(); +} + +void nrnthread_dat3_secmapping(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp) { + CellMapping* c = mapinfo.mapping[i_c]; + SecMapping* s = c->secmapping[i_sec]; + sclname = s->name; + nsec = s->nsec; + nseg = s->size(); + total_lfp_factors = s->seglfp_factors.size(); + n_electrodes = s->num_electrodes; + data_sec = s->sections; + data_seg = s->segments; + data_lfp = s->seglfp_factors; +} + // sizes and total data count int nrnthread_dat2_1(int tid, int& ncell, diff --git a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h index efce1359fb..b93b0cd53f 100644 --- a/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h +++ b/src/nrniv/nrncore_write/callbacks/nrncore_callbacks.h @@ -97,6 +97,18 @@ int nrnthread_dat2_vecplay_inst(int tid, int& last_index, int& discon_index, int& ubound_index); +void nrnthread_dat3_cell_count(int& cell_count); +void nrnthread_dat3_cellmapping(int i, int& gid, int& nsec, int& nseg, int& n_seclist); +void nrnthread_dat3_secmapping(int i_c, + int i_sec, + std::string& sclname, + int& nsec, + int& nseg, + size_t& total_lfp_factors, + int& n_electrodes, + std::vector& data_sec, + std::vector& data_seg, + std::vector& data_lfp); int* datum2int(int type, Memb_list* ml, @@ -215,6 +227,9 @@ static core2nrn_callback_t cnbs[] = { {"nrn2core_get_dat2_vecplay_", (CNB) nrnthread_dat2_vecplay}, {"nrn2core_get_dat2_vecplay_inst_", (CNB) nrnthread_dat2_vecplay_inst}, {"nrn2core_part2_clean_", (CNB) part2_clean}, + {"nrn2core_get_dat3_cell_count_", (CNB) nrnthread_dat3_cell_count}, + {"nrn2core_get_dat3_cellmapping_", (CNB) nrnthread_dat3_cellmapping}, + {"nrn2core_get_dat3_secmapping_", (CNB) nrnthread_dat3_secmapping}, {"nrn2core_get_trajectory_requests_", (CNB) nrnthread_get_trajectory_requests}, {"nrn2core_trajectory_values_", (CNB) nrnthread_trajectory_values}, diff --git a/src/nrniv/nrncore_write/io/nrncore_io.cpp b/src/nrniv/nrncore_write/io/nrncore_io.cpp index 4b3fa7f709..46e0913ce5 100644 --- a/src/nrniv/nrncore_write/io/nrncore_io.cpp +++ b/src/nrniv/nrncore_write/io/nrncore_io.cpp @@ -529,6 +529,10 @@ void write_nrnthread_task(const char* path, CellGroup* cgs, bool append) { /** @brief dump mapping information to gid_3.dat file */ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { + if (minfo.size() <= 0) { + return; + } + /** full path of mapping file */ std::stringstream ss; ss << path << "/" << gid << "_3.dat"; @@ -543,33 +547,54 @@ void nrn_write_mapping_info(const char* path, int gid, NrnMappingInfo& minfo) { fprintf(f, "%s\n", bbcore_write_version); /** number of gids in NrnThread */ - fprintf(f, "%zd\n", minfo.size()); + int count; + nrnthread_dat3_cell_count(count); + fprintf(f, "%zd\n", count); /** all cells mapping information in NrnThread */ - for (size_t i = 0; i < minfo.size(); i++) { - CellMapping* c = minfo.mapping[i]; - + for (size_t i = 0; i < count; i++) { + int cgid; + int t_sec; + int t_seg; + int n_seclist; + nrnthread_dat3_cellmapping(i, cgid, t_sec, t_seg, n_seclist); /** gid, #section, #compartments, #sectionlists */ - fprintf(f, "%d %d %d %zd\n", c->gid, c->num_sections(), c->num_segments(), c->size()); - - for (size_t j = 0; j < c->size(); j++) { - SecMapping* s = c->secmapping[j]; - size_t total_lfp_factors = s->seglfp_factors.size(); + fprintf(f, "%d %d %d %zd\n", cgid, t_sec, t_seg, n_seclist); + + for (size_t j = 0; j < n_seclist; j++) { + std::string sclname; + int nsec; + int nseg; + int n_electrodes; + size_t total_lfp_factors; + std::vector data_sec; + std::vector data_seg; + std::vector data_lfp; + nrnthread_dat3_secmapping(i, + j, + sclname, + nsec, + nseg, + total_lfp_factors, + n_electrodes, + data_sec, + data_seg, + data_lfp); /** section list name, number of sections, number of segments */ fprintf(f, "%s %d %zd %zd %d\n", - s->name.c_str(), - s->nsec, - s->size(), + sclname.c_str(), + nsec, + nseg, total_lfp_factors, - s->num_electrodes); + n_electrodes); /** section - segment mapping */ - if (s->size()) { - writeint(&(s->sections.front()), s->size()); - writeint(&(s->segments.front()), s->size()); + if (nseg) { + writeint(&(data_sec.front()), nseg); + writeint(&(data_seg.front()), nseg); if (total_lfp_factors) { - writedbl(&(s->seglfp_factors.front()), total_lfp_factors); + writedbl(&(data_lfp.front()), total_lfp_factors); } } } diff --git a/test/external/CMakeLists.txt b/test/external/CMakeLists.txt index 7a9c8f3e63..c4dfafe589 100644 --- a/test/external/CMakeLists.txt +++ b/test/external/CMakeLists.txt @@ -7,7 +7,7 @@ include(FetchContent) FetchContent_Declare( ringtest GIT_REPOSITORY https://github.com/neuronsimulator/ringtest - GIT_TAG ee24c8 + GIT_TAG d40caf64ac24c6ddcf2c082f4b0b7f7ca50657c9 SOURCE_DIR ${PROJECT_SOURCE_DIR}/external/tests/ringtest) FetchContent_Declare( diff --git a/test/external/ringtest/CMakeLists.txt b/test/external/ringtest/CMakeLists.txt index d082f1fd99..22edacc360 100644 --- a/test/external/ringtest/CMakeLists.txt +++ b/test/external/ringtest/CMakeLists.txt @@ -126,7 +126,46 @@ foreach(processor cpu gpu) ENVIRONMENT OMP_NUM_THREADS=${ringtest_num_threads} NEURON_INIT_MPI=1 COMMAND ${ringtest_python} -tstop 100 -coreneuron -nt ${ringtest_num_datasets} ${gpu_arg}) endforeach() + +nrn_add_test( + GROUP external_ringtest + NAME coreneuron_mpi_registermapping_directmode + REQUIRES coreneuron mpi python + PROCESSORS ${ringtest_mpi_ranks} + ENVIRONMENT NEURON_INIT_MPI=1 LIBSONATA_ZERO_BASED_GIDS=1 + COMMAND ${ringtest_special} -tstop 100 -coreneuron -registermapping) +nrn_add_test( + GROUP external_ringtest + NAME coreneuron_mpi_registermapping_filemode + REQUIRES coreneuron mpi python + PROCESSORS ${ringtest_mpi_ranks} + ENVIRONMENT NEURON_INIT_MPI=1 LIBSONATA_ZERO_BASED_GIDS=1 + COMMAND ${ringtest_special} -tstop 100 -coreneuron -registermapping -filemode) + # Step 3 -- add a job that compares the outputs of all the tests added in Step 2 nrn_add_test_group_comparison( GROUP external_ringtest REFERENCE_OUTPUT asciispikes::external/tests/ringtest/reference_data/spk1.100ms.std.ref) + +if(CORENRN_ENABLE_REPORTING) + find_program(H5DIFF_EXECUTABLE "h5diff") + if(H5DIFF_EXECUTABLE) + set(reference_path + "${PROJECT_BINARY_DIR}/test/external/tests/ringtest/reference_data/soma.ref.h5") + cpp_cc_build_time_copy( + INPUT "${PROJECT_SOURCE_DIR}/external/tests/ringtest/reference_data/soma.ref.h5" + OUTPUT "${reference_path}") + foreach(testfolder coreneuron_mpi_registermapping_directmode + coreneuron_mpi_registermapping_filemode) + add_test(NAME external_ringtest::compare_soma_report_${testfolder} + COMMAND "h5diff" ${PROJECT_BINARY_DIR}/test/external_ringtest/${testfolder}/soma.h5 + ${reference_path}) + set_tests_properties(external_ringtest::compare_soma_report_${testfolder} + PROPERTIES DEPENDS "external_ringtest::${testfolder}") + endforeach() + else() + message(WARNING "Can not find h5diff, skip comparing the soma report for " + "external_ringtest::coreneuron_mpi_registermapping_directmode " + "external_ringtest::coreneuron_mpi_registermapping_filemode") + endif() +endif()