Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default node order is cell contiguous (except for thread rootnodes). #3300

Draft
wants to merge 4 commits into
base: hines/cvlocal-bug
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/nrncvode/netcvode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1620,11 +1620,10 @@ bool NetCvode::init_global() {
// The sum of the ml[i].nodecount must equal the mechanism
// nodecount for the cell and each ml[i] data must be contiguous.
// Ideally the node permutation would be such that each cell
// is contiguous. So only needing a ml[0]. That is sadly not
// the case with the default permutation. The cell root nodes are
// all at the beginning, and thereafter only Section nodes are
// contiguous. It would be easy to permute nodes so that each cell
// is contiguous (except root node). This would result in a
// is contiguous. So only needing a ml[0]. This is now mostly
// the case with the default permutation. The root nodes are
// all at the beginning, and thereafter all the cell nodes are
// contiguous. This results in a
// CvMembList.ml.size() == 1 almost always with an exception of
// size() == 2 only for extracellular and for POINT_PROCESSes
// located both in the root node and other cell nodes.
Expand Down Expand Up @@ -1674,6 +1673,9 @@ bool NetCvode::init_global() {
// instance to cml->ml
cml->ml.emplace_back(cml->index);
assert(cml->ml.back().nodecount == 0);
// For the default node permutation, cell
// nodes are contiguous except for rootnode.
assert(cml->ml.size() < 3);
}
}

Expand Down
113 changes: 64 additions & 49 deletions src/nrnoc/multicore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,11 @@ void nrn_thread_memblist_setup() {
/* at the beginning of each thread region */
/* this differs from original secorder where all roots are at the beginning */
/* in passing, also set start and end indices. */
// Until 2025-01-03 I was under the misamprehension that (except for roots)
// this ordering kept cells contiguous. Lvardt sizes for CvMembList.ml disabused
// me of that. reorder_secorder now, in fact, keeps cells contiguous except
// for roots.
#include <queue> // for breadth first cell traversal without recursion

void reorder_secorder() {
Section *sec, *ch;
Expand All @@ -670,88 +675,98 @@ void reorder_secorder() {
/* roots of this thread */
sl = _nt->roots;
inode = 0;
// just for inode and rootnode threads.
ITERATE(qsec, sl) {
sec = hocSEC(qsec);
assert(sec->order == -1);
secorder[order] = sec;
sec->order = order;
++order;
nd = sec->parentnode;
nd->_nt = _nt;
inode += 1;
}
/* all children of what is already in secorder */
for (isec = order - _nt->ncell; isec < order; ++isec) {
sec = secorder[isec];
/* to make it easy to fill in PreSyn.nt_*/
sec->prop->dparam[9] = {neuron::container::do_not_search, _nt};
for (j = 0; j < sec->nnode; ++j) {
nd = sec->pnode[j];
nd->_nt = _nt;
inode += 1;
}
for (ch = sec->child; ch; ch = ch->sibling) {
assert(ch->order == -1);
secorder[order] = ch;
ch->order = order;

// To provide cell contiguity, need to traverse the sections
// of each cell. We choose to do this with breadth first traversal
// without recursion.
ITERATE(qsec, sl) {
Section* sec = hocSEC(qsec);
std::queue<Section*> que;
que.push(sec); // insert at end
while (!que.empty()) {
Section* sec = que.front(); // first element
que.pop(); // remove first element
for (auto ch = sec->child; ch; ch = ch->sibling) {
que.push(ch);
}
// process
assert(sec->order == -1);
/* to make it easy to fill in PreSyn.nt_*/
sec->prop->dparam[9] = {neuron::container::do_not_search, _nt};
secorder[order] = sec;
sec->order = order;
++order;
for (int j = 0; j < sec->nnode; ++j) {
auto nd = sec->pnode[j];
nd->_nt = _nt;
inode += 1;
}
}
}
_nt->end = inode;
CACHELINE_CALLOC(_nt->_v_node, Node*, inode);
CACHELINE_CALLOC(_nt->_v_parent, Node*, inode);
CACHELINE_CALLOC(_nt->_v_parent_index, int, inode);
}
/* do it again and fill _v_node and _v_parent */
/* re-traverse and fill _v_node and _v_parent */
/* index each cell section in relative order. Do offset later */
// ForAllSections(sec)
ITERATE(qsec, section_list) {
Section* sec = hocSEC(qsec);
sec->order = -1;
}
order = 0;
for (NrnThread* _nt: for_threads(nrn_threads, nrn_nthread)) {
/* roots of this thread */
sl = _nt->roots;
inode = 0;
// just the rootnodes
ITERATE(qsec, sl) {
sec = hocSEC(qsec);
assert(sec->order == -1);
secorder[order] = sec;
sec->order = order;
++order;
nd = sec->parentnode;
nd->_nt = _nt;
assert(nd->_nt == _nt);
_nt->_v_node[inode] = nd;
_nt->_v_parent[inode] = nullptr; // because this is a root node
_nt->_v_node[inode]->v_node_index = inode;
++inode;
}
/* all children of what is already in secorder */
for (isec = order - _nt->ncell; isec < order; ++isec) {
sec = secorder[isec];
/* to make it easy to fill in PreSyn.nt_*/
sec->prop->dparam[9] = {neuron::container::do_not_search, _nt};
for (j = 0; j < sec->nnode; ++j) {
nd = sec->pnode[j];
nd->_nt = _nt;
_nt->_v_node[inode] = nd;
if (j) {
_nt->_v_parent[inode] = sec->pnode[j - 1];
} else {
_nt->_v_parent[inode] = sec->parentnode;

ITERATE(qsec, sl) {
Section* sec = hocSEC(qsec);
std::queue<Section*> que;
que.push(sec); // insert at end
while (!que.empty()) {
Section* sec = que.front(); // first element
que.pop(); // remove first element
for (auto ch = sec->child; ch; ch = ch->sibling) {
que.push(ch);
}
_nt->_v_node[inode]->v_node_index = inode;
inode += 1;
}
for (ch = sec->child; ch; ch = ch->sibling) {
assert(ch->order == -1);
secorder[order] = ch;
ch->order = order;
// process
assert(sec->order == order);
/* to make it easy to fill in PreSyn.nt_*/
sec->prop->dparam[9] = {neuron::container::do_not_search, _nt};
assert(secorder[order] == sec);
assert(sec->order == order);
++order;
for (int j = 0; j < sec->nnode; ++j) {
auto nd = sec->pnode[j];
assert(nd->_nt == _nt);
_nt->_v_node[inode] = nd;
if (j) {
_nt->_v_parent[inode] = sec->pnode[j - 1];
} else {
_nt->_v_parent[inode] = sec->parentnode;
}
_nt->_v_node[inode]->v_node_index = inode;

inode += 1;
}
}
}
_nt->end = inode;
assert(_nt->end == inode);
}
assert(order == section_count);
/*assert(inode == v_node_count);*/
Expand Down
2 changes: 1 addition & 1 deletion test/external/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ include(FetchContent)
FetchContent_Declare(
ringtest
GIT_REPOSITORY https://github.com/neuronsimulator/ringtest
GIT_TAG 7d1aee72939f6dfcb2a14db4c7b00725d9e485fa
GIT_TAG b096d530e2636b34b79c812356979a0fa4e3323b
SOURCE_DIR ${PROJECT_SOURCE_DIR}/external/tests/ringtest)

FetchContent_Declare(
Expand Down
Loading