NEURON
extcelln.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 /* /local/src/master/nrn/src/nrnoc/extcell.cpp,v 1.4 1996/05/21 17:09:19 hines Exp */
3 
4 #include <stdio.h>
5 #include <math.h>
6 #include "section.h"
7 #include "nrniv_mf.h"
8 #include "hocassrt.h"
9 #include "parse.hpp"
10 
11 
12 extern int nrn_use_daspk_;
13 
14 #if EXTRACELLULAR
15 
17 
18 /* the N index is a keyword in the following. See init.cpp for implementation.*/
19 /* if nlayer is changed the symbol table arayinfo->sub[0] must be updated
20  for xraxial, xg, xc, and vext */
21 static const char* mechanism[] = {"0",
22  "extracellular",
23  "xraxial[N]",
24  "xg[N]",
25  "xc[N]",
26  "e_extracellular",
27  nullptr,
28 #if I_MEMBRANE
29  "i_membrane",
30  "sav_g",
31  "sav_rhs",
32 #endif
33  nullptr,
34  "vext[N]", // not handled with other mech data
35  nullptr};
36 static HocParmLimits limits[] = {{"xraxial", {1e-9, 1e15}},
37  {"xg", {0., 1e15}},
38  {"xc", {0., 1e15}},
39  {nullptr, {0., 0.}}};
40 
41 static HocParmUnits units[] = {{"xraxial", "MOhm/cm"},
42  {"xg", "S/cm2"},
43  {"xc", "uF/cm2"},
44  {"e_extracellular", "mV"},
45  {"vext", "mV"},
46  {"i_membrane", "mA/cm2"},
47  {0, 0}};
48 
49 static void extcell_alloc(Prop*);
50 static void extcell_init(neuron::model_sorted_token const&, NrnThread* nt, Memb_list* ml, int type);
51 #if 0
52 static void printnode(const char* s);
53 #endif
54 
55 static int _ode_count(int type) {
56  /* hoc_execerror("extracellular", "cannot be used with CVODE");*/
57  /* but can be used with daspk. However everything is handled
58  in analogy with intracellular nodes instead of in analogy with a
59  membrane mechanism.
60  Thus the count really comes from the size of sp13mat.
61 */
62  return 0;
63 }
64 
65 // 4: xraxial[nlayer], xg[nlayer], xc[nlayer], e_extracellular
66 constexpr static auto nparm = 4
67 #if I_MEMBRANE
68  + 3 // i_membrane, sav_g, and sav_rhs
69 #endif
70  ;
71 // it seems that one past the end (i.e. index nparm) is used to refer to vext, but that is
72 // allocated/managed separately; see neuron::extracellular::vext_pseudoindex() and its usage
73 
74 static void update_parmsize() {
76  // clang-format off
78  , field<double>{"xraxial", nrn_nlayer_extracellular}
79  , field<double>{"xg", nrn_nlayer_extracellular}
80  , field<double>{"xc", nrn_nlayer_extracellular}
81  , field<double>{"e_extracellular"}
82 #if I_MEMBRANE
83  , field<double>{"i_membrane"}
84  , field<double>{"sav_g"}
85  , field<double>{"sav_rhs"}
86 #endif
87  );
88  // clang-format on
89  int prop_size = nparm + 3 * (nrn_nlayer_extracellular - 1);
91 }
92 
93 static std::vector<double> param_default{
94  1e9, /* xraxial */
95  1e9, /* xg */
96  0.0, /* xc */
97  0.0, /* e_extracellular */
98 };
99 
100 extern "C" void extracell_reg_(void) {
101  register_mech(mechanism, extcell_alloc, nullptr, nullptr, nullptr, extcell_init, -1, 1);
102  int const i = nrn_get_mechtype(mechanism[1]);
103  assert(i == EXTRACELL);
105  hoc_register_cvode(i, _ode_count, nullptr, nullptr, nullptr);
108  update_parmsize();
109 }
110 
111 
112 /* solving is done with sparse13 */
113 
114 /* interface between hoc and extcell */
115 using namespace neuron::extracellular;
116 
117 /* based on update() in fadvance.cpp */
118 /* update has already been called so modify nd->v based on dvi
119 we only need to update extracellular
120 nodes and base the corresponding nd->v on dvm (dvm = dvi - dvx)
121 */
123  int i, cnt, il;
124  extern int secondorder;
125  Node *nd, **ndlist;
126  Extnode* nde;
127  Memb_list* ml = nt->_ecell_memb_list;
128  if (!ml) {
129  return;
130  }
131  cnt = ml->nodecount;
132  ndlist = ml->nodelist;
133 
134  for (i = 0; i < cnt; ++i) {
135  nd = ndlist[i];
136  nde = nd->extnode;
137  for (il = 0; il < nlayer; ++il) {
138  nde->v[il] += *nde->_rhs[il];
139  }
140  nd->v() -= *nde->_rhs[0];
141  }
142 
143 #if I_MEMBRANE
144  for (i = 0; i < cnt; ++i) {
145  nd = ndlist[i];
146  NODERHS(nd) -= *nd->extnode->_rhs[0];
147  ml->data(i, i_membrane_index) = ml->data(i, sav_g_index) * (NODERHS(nd)) +
148  ml->data(i, sav_rhs_index);
149 #if 1
150  /* i_membrane is a current density (mA/cm2). However
151  it contains contributions from Non-ELECTRODE_CURRENT
152  point processes. i_membrane(0) and i_membrane(1) will
153  return the membrane current density at the points
154  .5/nseg and 1-.5/nseg respectively. This can cause
155  confusion if non-ELECTRODE_CURRENT point processes
156  are located at these 0-area nodes since 1) not only
157  is the true current density infinite, but 2) the
158  correct absolute current is being computed here
159  at the x=1 point but is not available, and 3) the
160  correct absolute current at x=0 is not computed
161  if the parent is a rootnode or there is no
162  extracellular mechanism for the parent of this
163  section. Thus, if non-ELECTRODE_CURRENT point processesm,
164  eg synapses, are being used it is not a good idea to
165  insert them at the points x=0 or x=1
166  */
167 #else
168  ml->data(i, i_membrane_index) *= NODEAREA(nd);
169  /* i_membrane is nA for every segment. This is different
170  from all other continuous mechanism currents and
171  same as PointProcess currents since it contains
172  non-ELECTRODE_CURRENT point processes and may
173  be non-zero for the zero area nodes.
174  */
175 #endif
176  }
177 #endif
178 }
179 
180 static void extcell_alloc(Prop* p) {
181  assert(p->param_size() == (nparm - 3) + 3 * nrn_nlayer_extracellular);
182  assert(p->param_num_vars() == nparm);
183  for (auto i = 0; i < nrn_nlayer_extracellular; ++i) {
184  p->param(xraxial_index, i) = param_default[0];
185  p->param(xg_index, i) = param_default[1];
186  p->param(xc_index, i) = param_default[2];
187  }
189 }
190 
191 /*ARGSUSED*/
193  NrnThread* nt,
194  Memb_list* ml,
195  int type) {
196  int ndcount = ml->nodecount;
197  Node** ndlist = ml->nodelist;
198  if ((cvode_active_ > 0) && (nrn_use_daspk_ == 0)) {
199  hoc_execerror("Extracellular mechanism only works with fixed step methods and daspk", 0);
200  }
201  for (int i = 0; i < ndcount; ++i) {
202  for (int j = 0; j < nrn_nlayer_extracellular; ++j) {
203  ndlist[i]->extnode->v[j] = 0.;
204  }
205 #if I_MEMBRANE
206  ml->data(i, i_membrane_index) = 0.0;
207 #endif
208  }
209 }
210 
212  if (nde->v) {
213  free(nde->v); /* along with _a and _b */
214  free(nde->_d); /* along with _rhs, _a_matelm, _b_matelm, _x12, and _x21 */
215  nde->v = NULL;
216  nde->_a = NULL;
217  nde->_b = NULL;
218  nde->_d = NULL;
219  nde->_rhs = NULL;
220  nde->_a_matelm = NULL;
221  nde->_b_matelm = NULL;
222  nde->_x12 = NULL;
223  nde->_x21 = NULL;
224  }
225 }
226 
228  for (const Section* sec: range_sec(section_list)) {
229  if (sec->pnode[0]->extnode) {
230  hoc_execerror("Cannot change nlayer_extracellular when instances exist", NULL);
231  }
232  }
233 }
234 
235 static void update_existing_extnode(int old_nlayer) {
236  /*
237  This is a placeholder for a later extension to allow change
238  of nlayer even if the extracellular mechanism has been instantiated
239  in some sections. It is deferred for later because it is not clear
240  that handling pointer updates to vext is straightforward.
241  Hence the check_if_extracellular_in_use() function, which will
242  be eliminated when this is filled in.
243  */
244 }
245 
246 static void update_extracellular_reg(int old_nlayer) {
247  /* update hoc_symlist for arayinfo->sub[0] and u.rng.index. */
248  int i, ix;
249  Symbol* ecell = hoc_table_lookup("extracellular", hoc_built_in_symlist);
250  assert(ecell);
251  assert(ecell->type == MECHANISM);
252  ix = 0;
253  for (i = 0; i < ecell->s_varn; ++i) {
254  Symbol* s = ecell->u.ppsym[i];
255  if (s->type == RANGEVAR) {
256  Arrayinfo* a = s->arayinfo;
257  s->u.rng.index = ix;
258  if (a && a->nsub == 1) {
259  assert(a->sub[0] == old_nlayer);
260  a->sub[0] = nlayer;
261  ix += nlayer;
262  } else {
263  ix += 1;
264  }
265  }
266  }
267 }
268 
270  if (ifarg(1)) {
271  /* Note in section.h: #define nlayer (nrn_nlayer_extracellular) */
272  int old = nlayer;
273  nrn_nlayer_extracellular = (int) chkarg(1, 1., 1000.);
274  if (nrn_nlayer_extracellular == old) {
275  return;
276  }
278  update_parmsize();
279  /*global nlayer is the new value. Following needs to know the previous */
282  }
283  hoc_retpushx((double) nlayer);
284 }
285 
286 static void extnode_alloc_elements(Extnode* nde) {
288  if (nlayer > 0) {
289  nde->v = (double*) ecalloc(nlayer * 3, sizeof(double));
290  nde->_a = nde->v + nlayer;
291  nde->_b = nde->_a + nlayer;
292 
293  nde->_d = (double**) ecalloc(nlayer * 6, sizeof(double*));
294  nde->_rhs = nde->_d + nlayer;
295  nde->_a_matelm = nde->_rhs + nlayer;
296  nde->_b_matelm = nde->_a_matelm + nlayer;
297  nde->_x12 = nde->_b_matelm + nlayer;
298  nde->_x21 = nde->_x12 + nlayer;
299  }
300 }
301 
303  Extnode* nde;
304  Prop* p;
305  /* may be a nnode increase so some may already be allocated */
306  if (!nd->extnode) {
307  nde = new Extnode{};
309  nd->extnode = nde;
310  for (int j = 0; j < nlayer; ++j) {
311  nde->v[j] = 0.;
312  }
313  for (p = nd->prop; p; p = p->next) {
314  if (p->_type == EXTRACELL) {
315  for (auto i = 0; i < p->param_num_vars(); ++i) {
316  for (auto array_index = 0; array_index < p->param_array_dimension(i);
317  ++array_index) {
318  nde->param.push_back(p->param_handle(i, array_index));
319  }
320  }
321  break;
322  }
323  }
324  assert(p && p->_type == EXTRACELL);
325  }
326 }
327 
329  /* may be a nnode increase so some may already be allocated */
330  for (int i = sec->nnode - 1; i >= 0; i--) {
331  extcell_node_create(sec->pnode[i]);
332  }
333  /* if the rootnode is owned by this section then it gets extnode also*/
334  if (!sec->parentsec && sec->parentnode) { /* last "if" clause unnecessary*/
335  extcell_node_create(sec->parentnode);
336  }
337 }
338 
339 /* from treesetup.cpp */
340 void nrn_rhs_ext(NrnThread* _nt) {
341  int i, j, cnt;
342  Node *nd, *pnd, **ndlist;
343  Extnode *nde, *pnde;
344  Memb_list* ml = _nt->_ecell_memb_list;
345  if (!ml) {
346  return;
347  }
348  cnt = ml->nodecount;
349  ndlist = ml->nodelist;
350 
351  /* nd rhs contains -membrane current + stim current */
352  /* nde rhs contains stim current */
353  for (i = 0; i < cnt; ++i) {
354  nd = ndlist[i];
355  nde = nd->extnode;
356  *nde->_rhs[0] -= NODERHS(nd);
357 #if I_MEMBRANE
358  ml->data(i, sav_rhs_index) = *nde->_rhs[0];
359  /* and for daspk this is the ionic current which can be
360  combined later with i_cap before return from solve. */
361 #endif
362  }
363  for (i = 0; i < cnt; ++i) {
364  nd = ndlist[i];
365  nde = nd->extnode;
366  pnd = _nt->_v_parent[nd->v_node_index];
367  if (pnd) {
368  pnde = pnd->extnode;
369  /* axial contributions */
370  if (pnde) { /* parent sec may not be extracellular */
371  for (j = 0; j < nrn_nlayer_extracellular; ++j) {
372  double dv = pnde->v[j] - nde->v[j];
373  *nde->_rhs[j] -= nde->_b[j] * dv;
374  *pnde->_rhs[j] += nde->_a[j] * dv;
375  /* for the internal balance equation
376  take care of vi = vm + vx */
377  if (j == 0) {
378  NODERHS(nd) -= NODEB(nd) * dv;
379  NODERHS(pnd) += NODEA(nd) * dv;
380  }
381  }
382  } else { /* no extracellular in parent but still need
383  to take care of vi = vm + vx in this node.
384  Note that we are NOT grounding the parent
385  side of xraxial so equivalent to sealed.
386  Also note that when children of this node
387  do not have extracellular, we deal with
388  that, at end of this function. */
389  double dv = -nde->v[0];
390  NODERHS(nd) -= NODEB(nd) * dv;
391  NODERHS(pnd) += NODEA(nd) * dv;
392  }
393 
394  /* series resistance and battery to ground */
395  /* between nlayer-1 and ground */
397  *nde->_rhs[j] -= *nde->param[xg_index_ext(j)] *
398  (nde->v[j] - *nde->param[e_extracellular_index_ext()]);
399  for (--j; j >= 0; --j) { /* between j and j+1 layer */
400  double x = *nde->param[xg_index_ext(j)] * (nde->v[j] - nde->v[j + 1]);
401  *nde->_rhs[j] -= x;
402  *nde->_rhs[j + 1] += x;
403  }
404  }
405  }
406  cnt = _nt->_ecell_child_cnt;
407  for (i = 0; i < cnt; ++i) {
408  double dv;
409  nd = _nt->_ecell_children[i];
410  pnd = _nt->_v_parent[nd->v_node_index];
411  dv = pnd->extnode->v[0];
412  NODERHS(nd) -= NODEB(nd) * dv;
413  NODERHS(pnd) += NODEA(nd) * dv;
414  }
415 }
416 
418  int i, j, cnt;
419  Node *nd, *pnd, **ndlist;
420  double d, cfac, mfac;
421  Extnode *nde, *pnde;
422  Memb_list* ml = _nt->_ecell_memb_list;
423  if (!ml) {
424  return;
425  }
426  /*printnode("begin setup");*/
427  cnt = ml->nodecount;
428  ndlist = ml->nodelist;
429  cfac = .001 * _nt->cj;
430 
431  /* d contains all the membrane conductances (and capacitance) */
432  /* i.e. (cm/dt + di/dvm - dis/dvi)*[dvi] and
433  (dis/dvi)*[dvx] */
434  for (i = 0; i < cnt; ++i) {
435  nd = ndlist[i];
436  nde = nd->extnode;
437  d = NODED(nd);
438  /* nde->_d only has -ELECTRODE_CURRENT contribution */
439  d = (*nde->_d[0] += NODED(nd));
440  /* now d is only the membrane current contribution */
441  /* i.e. d = cm/dt + di/dvm */
442  *nde->_x12[0] -= d;
443  *nde->_x21[0] -= d;
444 #if I_MEMBRANE
445  ml->data(i, sav_g_index) = d;
446 #endif
447  }
448  /* series resistance, capacitance, and axial terms. */
449  for (i = 0; i < cnt; ++i) {
450  nd = ndlist[i];
451  nde = nd->extnode;
452  pnd = _nt->_v_parent[nd->v_node_index];
453  if (pnd) {
454  /* series resistance and capacitance to ground */
455  j = 0;
456  for (;;) { /* between j and j+1 layer */
457  mfac = (*nde->param[xg_index_ext(j)] + *nde->param[xc_index_ext(j)] * cfac);
458  *nde->_d[j] += mfac;
459  ++j;
460  if (j == nrn_nlayer_extracellular) {
461  break;
462  }
463  *nde->_d[j] += mfac;
464  *nde->_x12[j] -= mfac;
465  *nde->_x21[j] -= mfac;
466  }
467  pnde = pnd->extnode;
468  /* axial connections */
469  if (pnde) { /* parent sec may not be extracellular */
470  for (j = 0; j < nrn_nlayer_extracellular; ++j) {
471  *nde->_d[j] -= nde->_b[j];
472  *pnde->_d[j] -= nde->_a[j];
473  *nde->_a_matelm[j] += nde->_a[j];
474  *nde->_b_matelm[j] += nde->_b[j];
475  }
476  }
477  }
478  }
479  /*printnode("end setup_lhs");*/
480 }
481 
482 /* based on treeset.cpp */
483 void ext_con_coef(void) /* setup a and b */
484 {
485  int j, k;
486  double dx, area;
487  Node *nd, **pnd;
488  Extnode* nde;
489 
490  /* temporarily store half segment resistances in rhs */
491  for (Section* sec: range_sec(section_list)) {
492  if (sec->pnode[0]->extnode) {
493  dx = section_length(sec) / ((double) (sec->nnode - 1));
494  for (j = 0; j < sec->nnode - 1; j++) {
495  nde = sec->pnode[j]->extnode;
496  for (k = 0; k < nrn_nlayer_extracellular; ++k) {
497  *nde->_rhs[k] = 1e-4 * *nde->param[xraxial_index_ext(k)] *
498  (dx / 2.); /*Megohms*/
499  }
500  }
501  /* last segment has 0 length. */
502  nde = sec->pnode[j]->extnode;
503  for (k = 0; k < nrn_nlayer_extracellular; ++k) {
504  *nde->_rhs[k] = 0.;
505  *nde->param[xc_index_ext(k)] = 0.;
506  *nde->param[xg_index_ext(k)] = 0.;
507  }
508  /* if owns a rootnode */
509  if (!sec->parentsec) {
510  nde = sec->parentnode->extnode;
511  for (k = 0; k < nrn_nlayer_extracellular; ++k) {
512  *nde->_rhs[k] = 0.;
513  *nde->param[xc_index_ext(k)] = 0.;
514  *nde->param[xg_index_ext(k)] = 0.;
515  }
516  }
517  }
518  }
519  /* assume that if only one connection at x=1, then they butte
520  together, if several connections at x=1
521  then last point is at x=1, has 0 area and other points are at
522  centers of nnode-1 segments.
523  If interior connection then child half
524  section connects straight to the point*/
525  /* for the near future we always have a last node at x=1 with
526  no properties */
527  for (const Section* sec: range_sec(section_list)) {
528  if (sec->pnode[0]->extnode) {
529  /* node half resistances in general get added to the
530  node and to the node's "child node in the same section".
531  child nodes in different sections don't involve parent
532  node's resistance */
533  nde = sec->pnode[0]->extnode;
534  for (k = 0; k < nlayer; ++k) {
535  nde->_b[k] = *nde->_rhs[k];
536  }
537  for (j = 1; j < sec->nnode; j++) {
538  nde = sec->pnode[j]->extnode;
539  for (k = 0; k < nlayer; ++k) {
540  nde->_b[k] = *nde->_rhs[k] + *(sec->pnode[j - 1]->extnode->_rhs[k]); /*megohms*/
541  }
542  }
543  }
544  }
545  for (Section* sec: range_sec(section_list)) {
546  if (sec->pnode[0]->extnode) {
547  /* convert to siemens/cm^2 for all nodes except last
548  and microsiemens for last. This means that a*V = mamps/cm2
549  and a*v in last node = nanoamps. Note that last node
550  has no membrane properties and no area. It may perhaps recieve
551  current stimulus later */
552  /* first the effect of node on parent equation. Note That
553  last nodes have area = 1.e2 in dimensionless units so that
554  last nodes have units of microsiemens's */
555  pnd = sec->pnode;
556  nde = pnd[0]->extnode;
557  area = NODEAREA(sec->parentnode);
558  /* param[4] is rall_branch */
559  for (k = 0; k < nlayer; ++k) {
560  nde->_a[k] = -1.e2 * sec->prop->dparam[4].get<double>() / (nde->_b[k] * area);
561  }
562  for (j = 1; j < sec->nnode; j++) {
563  nde = pnd[j]->extnode;
564  area = NODEAREA(pnd[j - 1]);
565  for (k = 0; k < nlayer; ++k) {
566  nde->_a[k] = -1.e2 / (nde->_b[k] * area);
567  }
568  }
569  }
570  }
571  /* now the effect of parent on node equation. */
572  for (const Section* sec: range_sec(section_list)) {
573  if (sec->pnode[0]->extnode) {
574  for (j = 0; j < sec->nnode; j++) {
575  nd = sec->pnode[j];
576  nde = nd->extnode;
577  for (k = 0; k < nlayer; ++k) {
578  nde->_b[k] = -1.e2 / (nde->_b[k] * NODEAREA(nd));
579  }
580  }
581  }
582  }
583 }
584 
585 #if 0
586 /* needs to be fixed to deal with rootnodes having this property */
587 
588 static void printnode(const char* s) {
589  int in, i, j, k;
590  hoc_Item* qsec;
591  Section* sec;
592  Node* nd;
593  Extnode* nde;
594  double *pd;
595  NrnThread* _nt;
596  for (NrnThread* _nt : for_threads(nrn_threads, nrn_nthread)) for (in=0; in < _nt->end; ++in) {
597  nd = _nt->_v_node[in];
598  if (nd->extnode) {
599  sec = nd->sec;
600  j = nd->sec_node_index_;
601  nde = nd->extnode;
602  pd = nde->param;
603  for (k=0; k < nlayer; ++k) {
604 printf("%s %s nd%d layer%d v=%g rhs=%g:\n", s, secname(sec), j, k, nde->v[k], *nde->_rhs[k]);
605 printf("xraxial=%g xg=%g xc=%g e=%g\n", xraxial[k], xg[k], xc[k], e_extracellular);
606  }
607  }
608  }
609 }
610 #if 0
611 static int cntndsave;
612 static Extnode* ndesave;
613 
614 void save2mat(void) {
615  int i, j, k, im, ipm;
616  register Node *nd, *pnd;
617  register Extnode *nde, *pnde;
618 
619  if (cntndsave < v_node_count) {
620  if (ndesave) {
621  free(ndesave);
622  }
623  cntndsave = v_node_count;
624  ndesave = (Extnode*)ecalloc(cntndsave, sizeof(Extnode));
625  }
626  for (i=0; i < v_node_count; ++i) {
627  nd = v_node[i];
628  nde = nd->extnode;
629  if (nde) {
630  for (j=0; j < nlayer; ++j) {
631  ndesave[i].v[j] = nde->v[j];
632  ndesave[i].rhs[j] = nde->_rhs[j];
633  for (k=0; k < 2*(nlayer)+1; ++k) {
634  ndesave[i].m[j][k] = nde->_m[j][k];
635  }
636  if (!v_parent[i]->extnode && j > 0) {
637  ndesave[i].m[j][0] = 0.;
638  ndesave[i].m[j][2*(nlayer)] = 0.;
639  }
640  }
641  }else{
642  for (j=0; j < nlayer; ++j) {
643  ndesave[i].m[j][nlayer] = 1;
644  }
645  ndesave[i].v[0] = nd->v;
646  ndesave[i].m[0][nlayer] = NODED(nd);
647  ndesave[i].rhs[0] = NODERHS(nd);
648  ndesave[i].m[0][0] = NODEB(nd);
649  ndesave[i].m[0][2*(nlayer)] = NODEA(nd);
650  }
651  }
652 }
653 
654 #if 0
655 #define DBG printf
656 #else
657 DBG() {}
658 #endif
659 
660 void check2mat(void) {
661  int i, j, k, im, ip;
662  Node* nd;
663  Extnode* nde;
664  double sum;
665 
666  /* copy solved v to saved v */
667  for (i=0; i < v_node_count; ++ i) {
668  nd = v_node[i];
669  nde = nd->extnode;
670  if (nde) {
671  for (j=0; j < (nlayer); ++j) {
672  ndesave[i].v[j] = nde->_rhs[j];
673  }
674  }else{
675  ndesave[i].v[0] = NODERHS(nd);
676  }
677  }
678 
679 #if 0
680  printf("mat\n");
681  for (i=0; i < v_node_count; ++i) {
682  printf("node %d\n", i);
683  for (j=0; j < (nlayer); ++j) {
684  printf("%d %d ", j, i);
685  for (k=0; k <= 2*(nlayer); ++k) {
686  printf(" %-8.3g", ndesave[i].m[j][k]);
687  }
688  printf(" %g %g\n", ndesave[i].v[j], ndesave[i].rhs[j]);
689  }
690  }
691 #endif
692 
693  /* rhs - M*V accomplished by subtracting every term from rhs */
694  for (i=0; i < v_node_count; ++i) {
695 DBG("work on node %d\n", i);
696  if (v_parent[i]) {
697  ip = v_parent[i]->v_node_index;
698  /* effect of parent on node */
699 DBG(" effect of parent %d on node %d\n", ip, i);
700  for (j=0; j < nlayer; ++j) {
701 DBG(" work on layer %d\n", j);
702  for (k=j; k < nlayer; ++k) {
703 DBG(" nde[%d].rhs[%d] -= nde[%d].v[%d]*nde[%d].m[%d][%d]\n", i,j,ip,k,i,j,k-j);
704 DBG(" %g * %g\n",ndesave[ip].v[k],ndesave[i].m[j][k-j]);
705 ndesave[i].rhs[j] -= ndesave[ip].v[k]*ndesave[i].m[j][k-j];
706  }
707  }
708  /* effect of node on parent */
709 DBG(" effect of node %d on parent %d\n", i, ip);
710  for (j=0; j < nlayer; ++j) {
711 DBG(" work on layer %d\n", j);
712  for (k=0; k <= j; ++k) {
713 DBG(" nde[%d].rhs[%d] -= nde[%d].v[%d]*nde[%d].m[%d][%d]\n", ip,j,i,k,i,j,2*(nlayer)-j+k);
714 DBG(" %g * %g\n",ndesave[i].v[k],ndesave[i].m[j][2*(nlayer)-j+k]);
715 ndesave[ip].rhs[j] -= ndesave[i].v[k]*ndesave[i].m[j][2*(nlayer)-j+k];
716  }
717  }
718  }
719  /* effect of node on node */
720 DBG(" effect of node %d on node %d\n", i, i);
721  for (j=0; j < nlayer; ++j) {
722 DBG(" work on layer %d\n", j);
723  for (k=0; k < nlayer; ++k) {
724 DBG(" nde[%d].rhs[%d] -= nde[%d].v[%d]*nde[%d].m[%d][%d]\n", i,j,i,k,i,j,(nlayer)+k-j);
725 DBG(" %g * %g\n",ndesave[i].v[k],ndesave[i].m[j][(nlayer)+k-j]);
726 ndesave[i].rhs[j] -= ndesave[i].v[k]*ndesave[i].m[j][(nlayer)+k-j];
727  }
728  }
729  }
730 
731  for (i=0; i < v_node_count; ++i) {
732  for (j=0; j < (nlayer); ++j) {
733  if (fabs(ndesave[i].rhs[j]) > 1e-5) {
734  printf("bad soln of eq %d,%d rhs-M*V=%g\n",
735  i,j, ndesave[i].rhs[j]);
736  }
737  }
738  }
739 }
740 #endif
741 #endif
742 
743 
744 #endif /*EXTRACELLULAR*/
const char * secname(Section *sec)
name of section (for use in error messages)
Definition: cabcode.cpp:1674
double section_length(Section *sec)
Definition: cabcode.cpp:401
static Node * pnd
Definition: clamp.cpp:33
Symbol * hoc_table_lookup(const char *, Symlist *)
Definition: symbol.cpp:48
#define cnt
Definition: tqueue.hpp:44
#define area
Definition: md1redef.h:12
#define v
Definition: md1redef.h:11
#define extnode
Definition: md1redef.h:16
#define sec
Definition: md1redef.h:20
#define i
Definition: md1redef.h:19
static int _ode_count(int type)
Definition: extcelln.cpp:55
static HocParmUnits units[]
Definition: extcelln.cpp:41
void nrn_update_2d(NrnThread *nt)
Definition: extcelln.cpp:122
static void extcell_init(neuron::model_sorted_token const &, NrnThread *nt, Memb_list *ml, int type)
Definition: extcelln.cpp:192
static HocParmLimits limits[]
Definition: extcelln.cpp:36
constexpr static auto nparm
Definition: extcelln.cpp:66
int nrn_nlayer_extracellular
Definition: extcelln.cpp:16
void extracell_reg_(void)
Definition: extcelln.cpp:100
void ext_con_coef(void)
Definition: extcelln.cpp:483
void nlayer_extracellular()
Definition: extcelln.cpp:269
static void update_parmsize()
Definition: extcelln.cpp:74
static void extnode_alloc_elements(Extnode *nde)
Definition: extcelln.cpp:286
static std::vector< double > param_default
Definition: extcelln.cpp:93
static void extcell_alloc(Prop *)
Definition: extcelln.cpp:180
static void check_if_extracellular_in_use()
Definition: extcelln.cpp:227
void nrn_rhs_ext(NrnThread *_nt)
Definition: extcelln.cpp:340
void extcell_2d_alloc(Section *sec)
Definition: extcelln.cpp:328
int nrn_use_daspk_
Definition: treeset.cpp:59
static void update_extracellular_reg(int old_nlayer)
Definition: extcelln.cpp:246
void extcell_node_create(Node *nd)
Definition: extcelln.cpp:302
static const char * mechanism[]
Definition: extcelln.cpp:21
static void update_existing_extnode(int old_nlayer)
Definition: extcelln.cpp:235
void nrn_setup_ext(NrnThread *_nt)
Definition: extcelln.cpp:417
void extnode_free_elements(Extnode *nde)
Definition: extcelln.cpp:211
double chkarg(int, double low, double high)
Definition: code2.cpp:626
static RNG::key_type k
Definition: nrnran123.cpp:9
void hoc_retpushx(double x)
Definition: hocusr.cpp:154
#define assert(ex)
Definition: hocassrt.h:24
constexpr auto range_sec(hoc_List *iterable)
Definition: hoclist.h:50
#define rhs
Definition: lineq.h:6
#define EXTRACELL
Definition: membfunc.hpp:61
printf
Definition: extdef.h:5
fabs
Definition: extdef.h:3
auto for_threads(NrnThread *threads, int num_threads)
Definition: multicore.h:133
NrnThread * nrn_threads
Definition: multicore.cpp:56
int nrn_nthread
Definition: multicore.cpp:55
bool cvode_active_
Definition: netcvode.cpp:36
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
void * ecalloc(size_t n, size_t size)
Definition: nrnoc_aux.cpp:85
int nrn_get_mechtype(const char *name)
Get mechanism type by the mechanism name.
Definition: mk_mech.cpp:145
void hoc_register_prop_size(int, int, int)
int register_mech(const char **m, mod_alloc_t alloc, mod_f_t cur, mod_f_t jacob, mod_f_t stat, mod_f_t initialize, mod_f_t private_constructor, mod_f_t private_destructor, int nrnpointerindex, int vectorized)
std::size_t e_extracellular_index_ext()
Definition: membfunc.h:142
static constexpr auto xraxial_index
Definition: membfunc.h:124
static constexpr auto e_extracellular_index
Definition: membfunc.h:127
std::size_t xg_index_ext(std::size_t ilayer)
Definition: membfunc.h:136
static constexpr auto xg_index
Definition: membfunc.h:125
static constexpr auto xc_index
Definition: membfunc.h:126
static constexpr auto sav_rhs_index
Definition: membfunc.h:130
static constexpr auto sav_g_index
Definition: membfunc.h:129
static constexpr auto i_membrane_index
Definition: membfunc.h:128
std::size_t xc_index_ext(std::size_t ilayer)
Definition: membfunc.h:139
std::size_t xraxial_index_ext(std::size_t ilayer)
Definition: membfunc.h:133
static void register_data_fields(int mech_type, Fields const &... fields)
Type- and array-aware version of hoc_register_prop_size.
Definition: membfunc.h:235
static int prop_size
Definition: nocpout.cpp:189
size_t p
size_t j
s
Definition: multisend.cpp:521
int ifarg(int)
Definition: code.cpp:1607
short type
Definition: cabvars.h:10
hoc_List * section_list
Definition: init.cpp:113
void hoc_register_limits(int mechtype, HocParmLimits *limits)
Definition: init.cpp:1092
void hoc_register_cvode(int i, nrn_ode_count_t cnt, nrn_ode_map_t map, nrn_ode_spec_t spec, nrn_ode_matsol_t matsol)
Definition: init.cpp:995
void hoc_register_parm_default(int mechtype, const std::vector< double > *pd)
Definition: init.cpp:741
void hoc_register_units(int mechtype, HocParmUnits *units)
Definition: init.cpp:1109
#define EXTRACELLULAR
Definition: options.h:16
#define NODEAREA(n)
Definition: section.h:101
#define NODEA(n)
Definition: section_fwd.hpp:52
#define NODEB(n)
Definition: section_fwd.hpp:53
#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
Symlist * hoc_built_in_symlist
Definition: symbol.cpp:28
int nsub
Definition: hocdec.h:61
int sub[1]
Definition: hocdec.h:63
double ** _a_matelm
Definition: section_fwd.hpp:45
double ** _b_matelm
Definition: section_fwd.hpp:46
double * _b
Definition: section_fwd.hpp:42
double ** _x21
Definition: section_fwd.hpp:48
double ** _x12
Definition: section_fwd.hpp:47
std::vector< neuron::container::data_handle< double > > param
Definition: section_fwd.hpp:33
double * _a
Definition: section_fwd.hpp:41
double * v
Definition: section_fwd.hpp:40
double ** _d
Definition: section_fwd.hpp:43
double ** _rhs
Definition: section_fwd.hpp:44
A view into a set of mechanism instances.
Definition: nrnoc_ml.h:34
int nodecount
Definition: nrnoc_ml.h:78
Node ** nodelist
Definition: nrnoc_ml.h:68
std::vector< double * > data()
Get a vector of double* representing the model data.
Definition: memblist.cpp:64
Definition: section.h:105
Section * sec
Definition: section.h:193
int v_node_index
Definition: section.h:212
Extnode * extnode
Definition: section.h:199
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
double cj
Definition: multicore.h:61
int end
Definition: multicore.h:65
int _ecell_child_cnt
Definition: multicore.h:68
Node ** _v_parent
Definition: multicore.h:91
Node ** _ecell_children
Definition: multicore.h:96
Memb_list * _ecell_memb_list
Definition: multicore.h:95
Node ** _v_node
Definition: multicore.h:90
Definition: section.h:231
Definition: model.h:47
union Symbol::@28 u
Symbol ** ppsym
Definition: hocdec.h:125
short type
Definition: model.h:48
unsigned s_varn
Definition: hocdec.h:129