NEURON
solve.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 /* /local/src/master/nrn/src/nrnoc/solve.cpp,v 1.15 1999/07/12 14:34:13 hines Exp */
3 
4 /* solve.cpp 15-Dec-88 */
5 
6 /* The data structures in section.h were developed primarily for the needs of
7  solving and setting up the matrix equations reasonably efficiently in
8  space and time. I am hypothesizing that they will also be convenient
9  for accessing parameters.
10  Important properties of the structures used here are:
11  Each section must have at least one node. There may be 0 sections.
12  The order for back substitution is given by section[i]->order.
13  The order of last node to first node is used in triangularization.
14  First node to last is used in back substitution.
15 */
16 /* An equation is associated with each node. d and rhs are the diagonal and
17  right hand side respectively. a is the effect of this node on the parent
18  node's equation. b is the effect of the parent node on this node's
19  equation.
20  d is assumed to be non-zero.
21 */
22 
23 /* We have seen that it is best to have nodes generally denote the
24  centers of segments because most properties are most easily defined
25  at those points. The old problem then arises again of 2nd order correct
26  demands that a point be exactly at any branches. For this reason we
27  always allocate one extra node at the end with 0 length. Sections
28  that connect at x=1 connect to this node. Sections that connect
29  from 0<x<1 connect to nodes 0 to nnode-2 directly to the point
30  (no parent resistance, only a half segment resistance). It was an error
31  to try to connect a section to position 0. Now we are going to allow
32  that by the following artifice.
33 
34  Section 0 is always special. Its nodes act as roots to the independent
35  trees. They do not connect to each other. It has no properties.
36 */
37 
38 #if EXTRACELLULAR
39 /* Two users (Padjen and Shrager) require that the extracellular potential
40 be simulated. To do this we introduce a new node structure into the
41 section called extnode. This points to a vector of extnodes containing
42 the extracellular potential, the Node vector
43 is interpreted as the internal potential. Thus the membrane potential is
44 node[i].v - extnode[i].v[0]
45 */
46 /*
47 With vectorization, node.v is the membrane potential and node.rhs
48 after solving is the internal potential. Thus internal potential is
49 node.v + extnode.v[0]
50 */
51 
52 #endif
53 
54 #include "membdef.h"
55 #include "membfunc.h"
56 #include "nrniv_mf.h"
57 #include "nrnmpiuse.h"
58 #include "ocnotify.h"
59 #include "section.h"
60 #include "spmatrix.h"
61 #include "treeset.h"
62 
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <math.h>
66 
67 #include <utility>
68 
69 static void node_free();
70 static void triang(NrnThread*), bksub(NrnThread*);
71 
72 #if NRNMPI
73 void (*nrnmpi_splitcell_compute_)();
74 #endif
75 
77 extern void (*nrnpy_o2loc_p_)(Object*, Section**, double*);
78 extern void (*nrnpy_o2loc2_p_)(Object*, Section**, double*);
79 
80 /* used for vectorization and distance calculations */
83 
84 /* nrn_solve() solves the matrix equations represented in "section"
85  rhs is replaced by the solution (delta v's).
86  d is destroyed.
87 */
88 /* Section *sec_alloc(nsec) allocates a vector of nsec sections and returns a
89  pointer to the first one in the list. No nodes are allocated.
90  The usage is normally "section = sec_alloc(nsection)"
91  After allocation one must allocate nodes for each section with
92  node_alloc(sec, n).
93  After all this connect sections together using section indices.
94  Finally order the sections with section_order(section, nsec).
95  When the section vector is no longer needed (and before creating another
96  one) free the space with sec_free(section, nsection);
97 */
98 
99 #if DEBUGSOLVE
100 double debugsolve(void) /* returns solution error */
101 {
102  short inode;
103  Node *nd, *pnd, **ndP;
104  double err, sum;
105 
106  /* save parts of matrix that will be destroyed */
107  assert(0);
108  /* need to save the rootnodes too */
109  for (const Section* sec: range_sec(section_list)) {
110  assert(sec->pnode && sec->nnode);
111  for (inode = sec->nnode - 1; inode >= 0; inode--) {
112  nd = sec->pnode[inode];
113  nd->savd = NODED(nd);
114  nd->savrhs = NODERHS(nd);
115  }
116  }
117 
120 
121  err = 0.;
122  /* need to check the rootnodes too */
123  for (const Section* sec: range_sec(section_list)) {
124  for (inode = sec->nnode - 1; inode >= 0; inode--) {
125  ndP = sec->pnode + inode;
126  nd = sec->pnode[inode];
127  /* a single internal current equation */
128  sum = nd->savd * NODERHS(nd);
129  if (inode > 0) {
130  sum += NODEB(nd) * NODERHS(nd - 1);
131  } else {
132  pnd = sec->parentnode;
133  sum += NODEB(nd) * NODERHS(pnd);
134  }
135  if (inode < sec->nnode - 1) {
136  sum += NODEA(ndP[1]) * NODERHS(ndP[1]);
137  }
138  for (const Section* ch = nd->child; ch; ch = ch->sibling) {
139  pnd = ch->pnode[0];
140  assert(pnd && ch->nnode);
141  sum += NODEA(pnd) * NODERHS(pnd);
142  }
143  sum -= nd->savrhs;
144  err += fabs(sum);
145  }
146  }
147 
148  return err;
149 }
150 #endif /*DEBUGSOLVE*/
151 
152 
154  int inode;
155  double ratio;
156 
157  if (!sec || sec->parentnode == node) {
158  return 0.;
159  } else if ((inode = node->sec_node_index_) == sec->nnode - 1) {
160  ratio = 1.;
161  } else {
162  ratio = ((double) inode + .5) / ((double) sec->nnode - 1.);
163  }
164  return section_length(sec) * ratio;
165 }
166 
167 double topol_distance(Section* sec1,
168  Node* node1,
169  Section* sec2,
170  Node* node2,
171  Section** prootsec,
172  Node** prootnode) { /* returns the distance between the two nodes
173  Ie the sum of the appropriate length portions
174  of those sections connecting these two
175  nodes.
176  */
177  double d, x1, x2;
178 
179  d = 0;
180  if (tree_changed) {
181  setup_topology();
182  }
183  /* keep moving toward a common node */
184  while (sec1 != sec2) {
185  if (!sec1) {
186  d += node_dist(sec2, node2);
187  node2 = sec2->parentnode;
188  sec2 = sec2->parentsec;
189  } else if (!sec2) {
190  d += node_dist(sec1, node1);
191  node1 = sec1->parentnode;
192  sec1 = sec1->parentsec;
193  } else if (sec1->order > sec2->order) {
194  d += node_dist(sec1, node1);
195  node1 = sec1->parentnode;
196  sec1 = sec1->parentsec;
197  } else {
198  d += node_dist(sec2, node2);
199  node2 = sec2->parentnode;
200  sec2 = sec2->parentsec;
201  }
202  }
203  if (!sec1) {
204  if (node1 != node2) {
205  sec1 = 0;
206  d = 1e20;
207  node1 = (Node*) 0;
208  }
209  } else if (node1 != node2) {
210  x1 = node_dist(sec1, node1);
211  x2 = node_dist(sec2, node2);
212  if (x1 < x2) {
213  d += x2 - x1;
214  } else {
215  node1 = node2;
216  d += x1 - x2;
217  }
218  }
219  *prootsec = sec1;
220  *prootnode = node1;
221  return d;
222 }
223 
225 
226 void distance(void) {
227  double d, d_origin{};
228  int mode;
229  Node* node;
230  Section* sec;
231  static Node* origin_node;
232  Node* my_origin_node;
233  Section* my_origin_sec;
234 
235  if (tree_changed) {
236  setup_topology();
237  }
238 
239  if (ifarg(2)) {
240  nrn_seg_or_x_arg2(2, &sec, &d);
241  if (hoc_is_double_arg(1)) {
242  mode = (int) chkarg(1, 0., 1.);
243  } else {
244  mode = 2;
245  Object* o = *hoc_objgetarg(1);
246  my_origin_sec = (Section*) 0;
247  if (nrnpy_o2loc2_p_) {
248  (*nrnpy_o2loc2_p_)(o, &my_origin_sec, &d_origin);
249  }
250  if (!my_origin_sec) {
251  hoc_execerror("Distance origin not valid.",
252  "If first argument is an Object, it needs to be a Python Segment "
253  "object, a rxd.node object, or something with a segment property.");
254  }
255  my_origin_node = node_exact(my_origin_sec, d_origin);
256  }
257  } else if (ifarg(1)) {
258  nrn_seg_or_x_arg2(1, &sec, &d);
259  mode = 1;
260  } else {
261  sec = chk_access();
262  d = 0.;
263  mode = 0;
264  }
265  node = node_exact(sec, d);
266  if (mode == 0) {
267  origin_node = node;
268  origin_sec = sec;
269  } else {
270  if (mode != 2 && (!origin_sec || !origin_sec->prop)) {
271  hoc_execerror("Distance origin not valid.",
272  "Need to initialize origin with distance()");
273  }
274  if (mode == 1) {
275  my_origin_sec = origin_sec;
276  my_origin_node = origin_node;
277  }
278  d = topol_distance(my_origin_sec, my_origin_node, sec, node, &sec, &node);
279  }
280  hoc_retpushx(d);
281 }
282 
283 static void dashes(Section* sec, int offset, int first);
284 
285 void nrnhoc_topology(void) /* print the topology of the branched cable */
286 {
287  v_setup_vectors();
288  Printf("\n");
289  for (Section* sec: range_sec(section_list)) {
290  if (sec->parentsec == (Section*) 0) {
291  Printf("|");
292  dashes(sec, 0, '-');
293  }
294  }
295  Printf("\n");
296  hoc_retpushx(1.);
297 }
298 
299 static void dashes(Section* sec, int offset, int first) {
300  int i, scnt;
301  Section* ch;
302  char direc[30];
303  i = (int) nrn_section_orientation(sec);
304  Sprintf(direc, "(%d-%d)", i, 1 - i);
305  for (i = 0; i < offset; i++)
306  Printf(" ");
307  Printf("%c", first);
308  for (i = 2; i < sec->nnode; i++)
309  Printf("-");
310  if (sec->prop->dparam[4].get<double>() == 1) {
311  Printf("| %s%s\n", secname(sec), direc);
312  } else {
313  Printf("| %s%s with %g rall branches\n",
314  secname(sec),
315  direc,
316  sec->prop->dparam[4].get<double>());
317  }
318  /* navigate the sibling list backwards */
319  /* note that the sibling list is organized monotonically by
320  increasing distance from parent */
321  for (scnt = 0, ch = sec->child; ch; ++scnt, ch = ch->sibling) {
322  hoc_pushobj((Object**) ch);
323  }
324  while (scnt--) {
325  ch = (Section*) hoc_objpop();
327  Printf(" ");
328  dashes(ch, i + offset + 1, 0140); /* the ` char*/
329  }
330 }
331 
332 /* solve the matrix equation */
333 void nrn_solve(NrnThread* _nt) {
334 #if 0
335  printf("\nnrn_solve enter %lx\n", (long)_nt);
336  nrn_print_matrix(_nt);
337 #endif
338 #if NRNMPI
339  if (nrn_multisplit_solve_) {
340  nrn_thread_error("nrn_multisplit_solve");
341  (*nrn_multisplit_solve_)();
342  return;
343  }
344 #endif
345 
346 #if DEBUGSOLVE
347  {
348  double err;
349  nrn_thread_error("debugsolve");
350  err = debugsolve();
351  if (err > 1.e-10) {
352  Fprintf(stderr, "solve error = %g\n", err);
353  }
354  }
355 #else
356  if (use_sparse13) {
357  int e;
358  nrn_thread_error("solve use_sparse13");
360  e = spFactor(_nt->_sp13mat);
361  if (e != spOKAY) {
362  switch (e) {
363  case spZERO_DIAG:
364  hoc_execerror("spFactor error:", "Zero Diagonal");
365  case spNO_MEMORY:
366  hoc_execerror("spFactor error:", "No Memory");
367  case spSINGULAR:
368  hoc_execerror("spFactor error:", "Singular");
369  }
370  }
372  spSolve(_nt->_sp13mat, _nt->_sp13_rhs, _nt->_sp13_rhs);
375  } else {
376  triang(_nt);
377 #if NRNMPI
378  if (nrnmpi_splitcell_compute_) {
379  nrn_thread_error("nrnmpi_splitcell_compute");
380  (*nrnmpi_splitcell_compute_)();
381  }
382 #endif
383  bksub(_nt);
384  }
385 #endif
386 #if 0
387  printf("\nnrn_solve leave %lx\n", (long)_nt);
388  nrn_print_matrix(_nt);
389 #endif
390 }
391 
392 /* triangularization of the matrix equations */
393 static void triang(NrnThread* _nt) {
394  int i, i2, i3;
395  i2 = _nt->ncell;
396  i3 = _nt->end;
397  auto* const vec_a = _nt->node_a_storage();
398  auto* const vec_b = _nt->node_b_storage();
399  auto* const vec_d = _nt->node_d_storage();
400  auto* const vec_rhs = _nt->node_rhs_storage();
401  for (i = i3 - 1; i >= i2; --i) {
402  auto const p = vec_a[i] / vec_d[i];
403  auto const pi = _nt->_v_parent_index[i];
404  vec_d[pi] -= p * vec_b[i];
405  vec_rhs[pi] -= p * vec_rhs[i];
406  }
407 }
408 
409 /* back substitution to finish solving the matrix equations */
410 void bksub(NrnThread* _nt) {
411  auto const i1 = 0;
412  auto const i2 = i1 + _nt->ncell;
413  auto const i3 = _nt->end;
414  auto* const vec_b = _nt->node_b_storage();
415  auto* const vec_d = _nt->node_d_storage();
416  auto* const vec_rhs = _nt->node_rhs_storage();
417  for (int i = i1; i < i2; ++i) {
418  vec_rhs[i] /= vec_d[i];
419  }
420  for (int i = i2; i < i3; ++i) {
421  vec_rhs[i] -= vec_b[i] * vec_rhs[_nt->_v_parent_index[i]];
422  vec_rhs[i] /= vec_d[i];
423  }
424 }
425 
426 void nrn_clear_mark(void) {
427  for (Section* sec: range_sec(section_list)) {
428  sec->volatile_mark = 0;
429  }
430 }
432  return sec->volatile_mark++;
433 }
435  return sec->volatile_mark;
436 }
437 
438 /**
439  * @brief Allocate a new Section object.
440  *
441  * Changed from emalloc to allocation from a SectionPool in order to allow safe checking of whether
442  * a void* is a possible Section* without the possibility of invalid memory read errors. Note that
443  * freeing sections must be done with nrn_section_free(Section*).
444  */
446  return nrn_section_alloc();
447 }
448 
449 /* free a node vector for one section */
450 static void node_free(Section* sec) {
451  Node** pnd;
452 
453  pnd = sec->pnode;
454  if (!pnd) {
455  sec->nnode = 0;
456  }
457  if (sec->nnode == 0) {
458  return;
459  }
460  node_destruct(sec->pnode, sec->nnode);
461  sec->pnode = (Node**) 0;
462  sec->nnode = 0;
463 }
464 
465 static void section_unlink(Section* sec);
466 /* free everything about sections */
467 void sec_free(hoc_Item* secitem) {
468  Section* sec;
469 
470  if (!secitem) {
471  return;
472  }
473  sec = hocSEC(secitem);
474  assert(sec);
475  /*printf("sec_free %s\n", secname(sec));*/
477  if (auto* ob = sec->prop->dparam[6].get<Object*>();
478  ob && ob->secelm_ == secitem) { /* it is the last */
479  hoc_Item* q = secitem->prev;
480  if (q->itemtype && hocSEC(q)->prop && hocSEC(q)->prop->dparam[6].get<Object*>() == ob) {
481  ob->secelm_ = q;
482  } else {
483  ob->secelm_ = (hoc_Item*) 0;
484  }
485  }
486  hoc_l_delete(secitem);
487  prop_free(&(sec->prop));
488  node_free(sec);
489  if (!sec->parentsec && sec->parentnode) {
490  delete std::exchange(sec->parentnode, nullptr);
491  }
492 #if DIAMLIST
493  if (sec->pt3d) {
494  free((char*) sec->pt3d);
495  sec->pt3d = (Pt3d*) 0;
496  sec->npt3d = 0;
497  sec->pt3d_bsize = 0;
498  }
499  if (sec->logical_connection) {
500  free(sec->logical_connection);
501  sec->logical_connection = (Pt3d*) 0;
502  }
503 #endif
505 }
506 
507 
508 /* can't actually release the space till the refcount goes to 0 */
510  /*printf("section_unref %lx %d\n", (long)sec, sec->refcount-1);*/
511  if (--sec->refcount <= 0) {
512 #if 0
513 printf("section_unref: freed\n");
514 #endif
515  assert(!sec->parentsec);
517  }
518 }
519 
521  /*printf("section_ref %lx %d\n", (long)sec,sec->refcount+1);*/
522  ++sec->refcount;
523 }
524 
525 void nrn_sec_ref(Section** psec, Section* sec) {
526  Section* s = *psec;
527  if (sec) {
528  section_ref(sec);
529  }
530  *psec = sec;
531  if (s) {
532  section_unref(s);
533  }
534 }
535 
536 static void section_unlink(Section* sec) /* other sections no longer reference this one */
537 {
538  /* only sections that are explicitly connected to this are disconnected */
539  Section* child;
540  tree_changed = 1;
541  /* disconnect the sections connected to this at the parent end */
542  for (child = sec->child; child; child = child->sibling) {
543  nrn_disconnect(child);
544  }
546 }
547 
549  prop_free(&prop);
550 #if EXTRACELLULAR
551  if (extnode) {
553  }
554 #endif
555 #if EXTRAEQN
556  for (Eqnblock* e = eqnblock; e;) {
557  free(std::exchange(e, e->eqnblock_next));
558  }
559 #endif
560 #if EXTRACELLULAR
561  if (extnode) {
563  delete extnode;
564  }
565 #endif
566 }
567 
568 // this is delete[]...apart from the order?
569 void node_destruct(Node** pnode, int n) {
570  for (int i = n - 1; i >= 0; --i) {
571  delete std::exchange(pnode[i], nullptr);
572  }
573  delete[] std::exchange(pnode, nullptr);
574 }
575 
576 #if KEEP_NSEG_PARM
577 
578 extern int keep_nseg_parm_;
579 
580 // TODO this logic should just be in the copy constructor...probably some of it
581 // already does the right thing by default
582 static Node* node_clone(Node* nd1) {
583  Node* nd2 = new Node{};
584  nd2->v() = nd1->v();
585  for (Prop* p1 = nd1->prop; p1; p1 = p1->next) {
586  if (!memb_func[p1->_type].is_point) {
587  Prop* p2 = prop_alloc(&(nd2->prop), p1->_type, nd2);
588  if (p2->ob) {
589  Symbol *s, *ps;
590  double *px, *py;
591  int j, jmax;
592  s = memb_func[p1->_type].sym;
593  jmax = s->s_varn;
594  for (j = 0; j < jmax; ++j) {
595  ps = s->u.ppsym[j];
596  px = p2->ob->u.dataspace[ps->u.rng.index].pval;
597  py = p1->ob->u.dataspace[ps->u.rng.index].pval;
598  std::size_t imax{hoc_total_array_data(ps, 0)};
599  for (std::size_t i = 0; i < imax; ++i) {
600  px[i] = py[i];
601  }
602  }
603  } else {
604  for (int i = 0; i < p1->param_num_vars(); ++i) {
605  for (auto j = 0; j < p1->param_array_dimension(i); ++j) {
606  p2->param(i, j) = p1->param(i, j);
607  }
608  }
609  }
610  }
611  }
612  /* in case the user defined an explicit ion_style, make sure
613  the new node has the same style for all ions. */
614  for (Prop* p1 = nd1->prop; p1; p1 = p1->next) {
615  if (nrn_is_ion(p1->_type)) {
616  Prop* p2 = nd2->prop;
617  while (p2 && p2->_type != p1->_type) {
618  p2 = p2->next;
619  }
620  assert(p2 && p1->_type == p2->_type);
621  p2->dparam[0] = p1->dparam[0].get<int>();
622  }
623  }
624 
625  return nd2;
626 }
627 
628 static void node_realloc(Section* sec, short nseg) {
629  Node **pn1, **pn2;
630  int n1, n2, i1, i2;
631  double x;
632  pn1 = sec->pnode;
633  n1 = sec->nnode;
634  pn2 = new Node* [nseg] {};
635  n2 = nseg;
636  sec->pnode = pn2;
637  sec->nnode = n2;
638 
639  n1--;
640  n2--; /* number of non-zero area segments */
641  pn2[n2] = pn1[n1]; /* 0 area node at end of section */
642  pn1[n1] = (Node*) 0;
643 
644  /* sprinkle nodes from pn1 to pn2 */
645  if (n1 < n2) {
646  /* the ones that are reused */
647  for (i1 = 0; i1 < n1; ++i1) {
648  x = (i1 + .5) / (double) n1;
649  i2 = (int) (n2 * x); /* because we want to round to n2*x-.5 */
650  pn2[i2] = pn1[i1];
651  }
652  /* the ones that are cloned */
653  for (i2 = 0; i2 < n2; ++i2)
654  if (pn2[i2] == NULL) {
655  x = (i2 + 0.5) / (double) n2;
656  i1 = (int) (n1 * x);
657  pn2[i2] = node_clone(pn1[i1]);
658  }
659  for (i1 = 0; i1 < n1; ++i1) {
660  pn1[i1] = (Node*) 0;
661  }
662  } else {
663  for (i2 = 0; i2 < n2; ++i2) {
664  x = (i2 + .5) / (double) n2;
665  i1 = (int) (n1 * x);
666  pn2[i2] = pn1[i1];
667  pn1[i1] = (Node*) 0;
668  }
669  /* do not lose any point processes */
670  i1 = 0;
671  for (i2 = 0; i2 < n2; ++i2) {
672  double x1, x2;
673  x2 = (i2 + 1.) / (double) n2; /* far end of new segment */
674  for (; i1 < n1; ++i1) {
675  x1 = (i1 + .5) / (double) n1;
676  if (x1 > x2) {
677  break;
678  }
679  if (pn1[i1] == (Node*) 0) {
680  continue;
681  }
682 #if 0
683 printf("moving point processes from pn1[%d] to pn2[%d]\n", i1, i2);
684 printf("i.e. x1=%g in the range %g to %g\n", x1, x2-1./n2, x2);
685 #endif
686  nrn_relocate_old_points(sec, pn1[i1], sec, pn2[i2]);
687  }
688  }
689  /* Some of the pn1 were not used */
690  }
691  node_destruct(pn1, n1 + 1);
692  for (i2 = 0; i2 < nseg; ++i2) {
693  pn2[i2]->sec_node_index_ = i2;
694  }
695 #if EXTRACELLULAR
696  if (sec->pnode[sec->nnode - 1]->extnode) {
698  }
699 #endif
700 }
701 
702 #endif
703 
704 /* allocate node vectors for a section list */
705 void node_alloc(Section* sec, short nseg) {
706  int i;
707 #if KEEP_NSEG_PARM
708  if (keep_nseg_parm_ && (nseg > 0) && sec->pnode) {
709  node_realloc(sec, nseg);
710  } else
711 #endif
712  {
713  node_free(sec);
714  if (nseg == 0) {
715  return;
716  }
717  sec->pnode = new Node* [nseg] {};
718  sec->nnode = nseg;
719  for (i = 0; i < nseg; ++i) {
720  sec->pnode[i] = new Node{};
721  sec->pnode[i]->sec_node_index_ = i;
722  }
723  }
724  for (i = 0; i < nseg; ++i) {
725  sec->pnode[i]->sec = sec;
726  }
727 }
728 
729 void section_order(void) /* create a section order consistent */
730  /* with connection info */
731 {
732  int order, isec;
733  Section* ch;
734 
735  /* count the sections */
736  section_count = 0;
737  for (Section* sec: range_sec(section_list)) {
738  sec->order = -1;
739  ++section_count;
740  }
741 
742  if (secorder) {
743  free((char*) secorder);
744  secorder = (Section**) 0;
745  }
746  if (section_count) {
747  secorder = (Section**) emalloc(section_count * sizeof(Section*));
748  }
749  order = 0;
750  for (Section* sec: range_sec(section_list)) {
751  if (!sec->parentsec) {
752  secorder[order] = sec;
753  sec->order = order;
754  ++order;
755  }
756  }
757 
758  for (isec = 0; isec < section_count; isec++) {
759  if (isec >= order) {
760  for (Section* sec: range_sec(section_list)) {
761  Section *psec, *s = sec;
762  for (psec = sec->parentsec; psec; s = psec, psec = psec->parentsec) {
763  if (!psec || s->order >= 0) {
764  break;
765  } else if (psec == sec) {
766  fprintf(stderr, "A loop exists consisting of:\n %s", secname(sec));
767  for (s = sec->parentsec; s != sec; s = s->parentsec) {
768  fprintf(stderr, " %s", secname(s));
769  }
770  fprintf(stderr,
771  " %s\nUse <section> disconnect() to break the loop\n ",
772  secname(sec));
773  hoc_execerror("A loop exists involving section", secname(sec));
774  }
775  }
776  }
777  }
778  Section* sec = secorder[isec];
779  for (ch = sec->child; ch; ch = ch->sibling) {
780  secorder[order] = ch;
781  ch->order = order;
782  ++order;
783  }
784  }
786 }
double nrn_section_orientation(Section *sec)
Definition: cabcode.cpp:1556
Section * chk_access()
Definition: cabcode.cpp:449
const char * secname(Section *sec)
name of section (for use in error messages)
Definition: cabcode.cpp:1674
int node_index_exact(Section *sec, double x)
return -1 if x at connection end, nnode-1 if at other end
Definition: cabcode.cpp:1426
void setup_topology(void)
Definition: cabcode.cpp:1635
double section_length(Section *sec)
Definition: cabcode.cpp:401
double nrn_connection_position(Section *sec)
Definition: cabcode.cpp:1552
int tree_changed
Definition: cabcode.cpp:51
void nrn_disconnect(Section *sec)
Definition: cabcode.cpp:593
Node * node_exact(Section *sec, double x)
like node_index but give proper node when x is 0 or 1 as well as in between
Definition: cabcode.cpp:1800
static Node * pnd
Definition: clamp.cpp:33
#define sec
Definition: md1redef.h:20
#define i
Definition: md1redef.h:19
#define prop
Definition: md1redef.h:38
static double order(void *v)
Definition: cvodeobj.cpp:218
Section * nrn_section_alloc()
Definition: cxprop.cpp:85
void nrn_section_free(Section *s)
Definition: cxprop.cpp:95
void extcell_2d_alloc(Section *sec)
Definition: extcelln.cpp:328
void extnode_free_elements(Extnode *nde)
Definition: extcelln.cpp:211
double chkarg(int, double low, double high)
Definition: code2.cpp:626
void nrn_print_matrix(NrnThread *_nt)
Definition: fadvance.cpp:664
size_t hoc_total_array_data(const Symbol *s, Objectdata *obd)
Definition: hoc_oop.cpp:95
void hoc_pushobj(Object **d)
Definition: code.cpp:784
int hoc_is_double_arg(int narg)
Definition: code.cpp:864
void hoc_retpushx(double x)
Definition: hocusr.cpp:154
#define assert(ex)
Definition: hocassrt.h:24
#define hocSEC(q)
Definition: hoclist.h:87
void hoc_l_delete(hoc_Item *)
constexpr auto range_sec(hoc_List *iterable)
Definition: hoclist.h:50
Object ** hoc_objgetarg(int)
Definition: code.cpp:1614
void notify_freed_val_array(double *p, size_t size)
Definition: ivoc.cpp:152
printf
Definition: extdef.h:5
fabs
Definition: extdef.h:3
NrnThread * nrn_threads
Definition: multicore.cpp:56
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
static void * emalloc(size_t size)
Definition: mpispike.cpp:30
int nrn_is_ion(int)
auto *const vec_d
Definition: cellorder.cpp:615
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
auto *const vec_b
Definition: cellorder.cpp:614
auto *const vec_rhs
Definition: cellorder.cpp:616
Prop * prop_alloc(Prop **, int, Node *)
Definition: treeset.cpp:671
void v_setup_vectors()
Definition: treeset.cpp:1596
void nrn_seg_or_x_arg2(int iarg, Section **psec, double *px)
Definition: point.cpp:186
void nrn_relocate_old_points(Section *oldsec, Node *oldnode, Section *sec, Node *node)
Definition: point.cpp:153
void prop_free(Prop **)
Definition: treeset.cpp:710
static Node * node(Object *)
Definition: netcvode.cpp:291
int const size_t const size_t n
Definition: nrngsl.h:10
size_t q
size_t p
size_t j
s
Definition: multisend.cpp:521
int ifarg(int)
Definition: code.cpp:1607
std::vector< Memb_func > memb_func
Definition: init.cpp:145
hoc_List * section_list
Definition: init.cpp:113
void nrn_thread_error(const char *s)
Definition: multicore.cpp:297
int keep_nseg_parm_
Definition: cabcode.cpp:1474
Section ** secorder
Definition: solve.cpp:82
void distance(void)
Definition: solve.cpp:226
void nrn_clear_mark(void)
Definition: solve.cpp:426
static Section * origin_sec
Definition: solve.cpp:224
void(* nrnpy_o2loc2_p_)(Object *, Section **, double *)
Definition: point.cpp:31
void nrn_sec_ref(Section **psec, Section *sec)
Definition: solve.cpp:525
void sec_free(hoc_Item *secitem)
Definition: solve.cpp:467
short nrn_value_mark(Section *sec)
Definition: solve.cpp:434
void(* nrn_multisplit_solve_)()
Definition: solve.cpp:76
static void node_realloc(Section *sec, short nseg)
Definition: solve.cpp:628
double node_dist(Section *sec, Node *node)
Definition: solve.cpp:153
void nrnhoc_topology(void)
Definition: solve.cpp:285
static void node_free()
void(* nrnpy_o2loc_p_)(Object *, Section **, double *)
Definition: point.cpp:30
static Node * node_clone(Node *nd1)
Definition: solve.cpp:582
int section_count
Definition: solve.cpp:81
void section_unref(Section *sec)
Definition: solve.cpp:509
double topol_distance(Section *sec1, Node *node1, Section *sec2, Node *node2, Section **prootsec, Node **prootnode)
Definition: solve.cpp:167
short nrn_increment_mark(Section *sec)
Definition: solve.cpp:431
void section_order(void)
Definition: solve.cpp:729
static void triang(NrnThread *)
Definition: solve.cpp:393
void node_destruct(Node **pnode, int n)
Definition: solve.cpp:569
void nrn_solve(NrnThread *_nt)
Definition: solve.cpp:333
Section * sec_alloc()
Allocate a new Section object.
Definition: solve.cpp:445
void node_alloc(Section *sec, short nseg)
Definition: solve.cpp:705
void section_ref(Section *sec)
Definition: solve.cpp:520
static void section_unlink(Section *sec)
Definition: solve.cpp:536
static void dashes(Section *sec, int offset, int first)
Definition: solve.cpp:299
static void bksub(NrnThread *)
Definition: solve.cpp:410
Object ** hoc_objpop()
Pop pointer to object pointer and return top elem from stack.
Definition: code.cpp:943
static void pnode(Prop *)
Definition: psection.cpp:47
#define NODEA(n)
Definition: section_fwd.hpp:52
#define NODEB(n)
Definition: section_fwd.hpp:53
int use_sparse13
Definition: treeset.cpp:58
#define NODERHS(n)
Definition: section_fwd.hpp:55
#define nlayer
Definition: section_fwd.hpp:31
#define NODED(n)
Definition: section_fwd.hpp:54
#define NULL
Definition: spdefs.h:105
int spFactor(char *eMatrix)
Definition: spfactor.cpp:307
#define spNO_MEMORY
Definition: spmatrix.h:90
#define spZERO_DIAG
Definition: spmatrix.h:88
void spSolve(char *eMatrix, spREAL *RHS, spREAL *Solution, std::optional< spREAL * > iRHS=std::nullopt, std::optional< spREAL * > iSolution=std::nullopt)
Definition: spsolve.cpp:104
#define spSINGULAR
Definition: spmatrix.h:89
#define spOKAY
Definition: spmatrix.h:86
double * v
Definition: section_fwd.hpp:40
Definition: section.h:105
Extnode * extnode
Definition: section.h:199
~Node()
Definition: solve.cpp:548
Section * child
Definition: section.h:191
auto & v()
Definition: section.h:141
int sec_node_index_
Definition: section.h:213
Prop * prop
Definition: section.h:190
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int * _v_parent_index
Definition: multicore.h:89
int ncell
Definition: multicore.h:64
double * node_a_storage()
Definition: multicore.cpp:1054
char * _sp13mat
Definition: multicore.h:94
double * node_rhs_storage()
Definition: multicore.cpp:1074
int end
Definition: multicore.h:65
double * node_d_storage()
Definition: multicore.cpp:1069
double * _sp13_rhs
Definition: multicore.h:92
double * node_b_storage()
Definition: multicore.cpp:1064
Definition: hocdec.h:173
Objectdata * dataspace
Definition: hocdec.h:177
hoc_Item * secelm_
Definition: hocdec.h:184
union Object::@47 u
Definition: section.h:231
Datum * dparam
Definition: section.h:247
short _type
Definition: section.h:244
double & param(int field_index, int array_index=0)
Return a reference to the i-th floating point data field associated with this Prop.
Definition: section.h:294
Object * ob
Definition: section.h:252
Prop * next
Definition: section.h:243
Definition: section.h:45
Section * sibling
Definition: section.h:56
int order
Definition: section.h:60
Prop * prop
Definition: section.h:71
Node * parentnode
Definition: section.h:58
Section * parentsec
Definition: section.h:53
Definition: model.h:47
union Symbol::@28 u
struct Symbol::@45::@46 rng
hoc_Item * prev
Definition: hoclist.h:45
T get() const
Explicit conversion to any T.
void update_actual_d_based_on_sp13_mat(NrnThread *nt)
Definition: treeset.cpp:354
void update_actual_rhs_based_on_sp13_rhs(NrnThread *nt)
Definition: treeset.cpp:336
void update_sp13_rhs_based_on_actual_rhs(NrnThread *nt)
Definition: treeset.cpp:345
void update_sp13_mat_based_on_actual_d(NrnThread *nt)
Definition: treeset.cpp:363
double * pval
Definition: hocdec.h:164
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8
int Printf(const char *fmt, Args... args)
Definition: logger.hpp:18