NEURON
ldifus.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <math.h>
6 #include "section.h"
7 #include "membfunc.h"
8 #include "neuron.h"
9 #include "nrniv_mf.h"
10 #include "parse.hpp"
11 
12 
13 #define nt_t nrn_threads->_t
14 #define nt_dt nrn_threads->_dt
15 
16 extern "C" int diam_change_cnt;
17 extern "C" int structure_change_cnt;
18 
19 typedef struct LongDifus {
20  int dchange;
21  int* mindex; /* index into memb_list[m] */
22  int* pindex; /* parent in this struct */
23  double** state;
24  double* a;
25  double* b;
26  double* d;
27  double* rhs;
28  double* af; /* efficiency benefit is very low, rall/dx^2 */
29  double* bf; /* 1/dx^2 */
30  double* vol; /* volatile volume from COMPARTMENT */
31  double* dc; /* volatile diffusion constant * cross sectional
32  area from LONGITUDINAL_DIFFUSION */
34 
35 typedef struct LongDifusThreadData {
36  int nthread;
40 
41 static int ldifusfunccnt;
43 
45 
49  ++ldifusfunccnt;
50 }
51 
52 #if MAC
53 /* this avoids a missing _ptrgl12 in the mac library that was called by
54 the MrC compiled object
55 */
56 void mac_difusfunc(ldifusfunc2_t* f,
57  int m,
58  ldifusfunc3_t diffunc,
59  void** v,
60  int ai,
61  int sindex,
62  int dindex,
63  NrnThread* nt) {
64  (*f)(m, diffunc, v, ai, sindex, dindex, nt);
65 }
66 #endif
67 
68 
69 extern "C" void nrn_tree_solve(double* a, double* d, double* b, double* rhs, int* pindex, int n) {
70  /*
71  treesolver
72 
73  a - above the diagonal
74  d - diagonal
75  b - below the diagonal
76  rhs - right hand side, which is changed to the result
77  pindex - parent indices
78  n - number of states
79  */
80 
81  int i;
82 
83  /* triang */
84  for (i = n - 1; i > 0; --i) {
85  int pin = pindex[i];
86  if (pin > -1) {
87  double p;
88  p = a[i] / d[i];
89  d[pin] -= p * b[i];
90  rhs[pin] -= p * rhs[i];
91  }
92  }
93  /* bksub */
94  for (i = 0; i < n; ++i) {
95  int pin = pindex[i];
96  if (pin > -1) {
97  rhs[i] -= b[i] * rhs[pin];
98  }
99  rhs[i] /= d[i];
100  }
101 }
102 
103 
104 void long_difus_solve(int method, NrnThread* nt) {
105  ldifusfunc2_t* f = NULL;
106  int i;
107  if (ldifusfunc) {
108  switch (method) {
109  case 0: /* normal staggered time step */
110  f = stagger;
111  break;
112  case 1: /* dstate = f(state) */
113  f = ode;
114  break;
115  case 2: /* solve (1 + dt*jacobian)*x = b */
116  f = matsol;
117  break;
118  case 3: /* setup only called by thread 0 */
119  f = overall_setup;
120  break;
121  }
122  assert(f);
123 
124  for (i = 0; i < ldifusfunccnt; ++i) {
125  (*ldifusfunc[i])(f, nt);
126  }
127  }
128 }
129 
130 static void longdifusfree(LongDifus** ppld) {
131  if (*ppld) {
132  LongDifus* pld = *ppld;
133 #if 0
134 printf("free longdifus structure_change=%d %d\n", pld->schange, structure_change_cnt);
135 #endif
136  free(pld->mindex);
137  free(pld->pindex);
138  free(pld->state);
139  free(pld->a);
140  free(pld->b);
141  free(pld->d);
142  free(pld->rhs);
143  free(pld->af);
144  free(pld->bf);
145  free(pld->vol);
146  free(pld->dc);
147  free(pld);
148  }
149  *ppld = (LongDifus*) 0;
150 }
151 
152 static void longdifus_diamchange(LongDifus* pld, int m, int sindex, Memb_list* ml, NrnThread* _nt) {
153  int i, n, mi, mpi, j, index, pindex, vnodecount;
154  Node *nd, *pnd;
155  double rall, dxp, dxc;
156 
157  if (pld->dchange == diam_change_cnt) {
158  return;
159  }
160  /*printf("longdifus_diamchange %d %d\n", pld->dchange, diam_change_cnt);*/
161  vnodecount = _nt->end;
162  n = ml->nodecount;
163 
164  for (i = 0; i < n; ++i) {
165  /* For every child with a parent having this mechanism */
166  /* Also child may butte end to end with parent or attach to middle */
167  mi = pld->mindex[i];
168  if (sindex < 0) {
169  pld->state[i] = ml->pdata[mi][-sindex - 1].pval;
170  } else {
171  pld->state[i] = ml->data[mi] + sindex;
172  }
173  nd = ml->nodelist[mi];
174  pindex = pld->pindex[i];
175  if (pindex > -1) {
176  mpi = pld->mindex[pindex];
177  pnd = ml->nodelist[mpi];
178  if (nd->sec_node_index_ == 0) {
179  rall = nd->sec->prop->dparam[4].val;
180  } else {
181  rall = 1.;
182  }
183  dxc = section_length(nd->sec) / ((double) (nd->sec->nnode - 1));
184  dxp = section_length(pnd->sec) / ((double) (pnd->sec->nnode - 1));
185  pld->af[i] = 2 * rall / dxp / (dxc + dxp);
186  pld->bf[i] = 2 / dxc / (dxc + dxp);
187  }
188  }
189  pld->dchange = diam_change_cnt;
190 }
191 
192 static void longdifusalloc(LongDifus** ppld, int m, int sindex, Memb_list* ml, NrnThread* _nt) {
193  LongDifus* pld;
194  int i, n, mi, mpi, j, index, pindex, vnodecount;
195  int *map, *omap;
196  Node *nd, *pnd;
197  hoc_Item* qsec;
198 
199  vnodecount = _nt->end;
200  *ppld = pld = (LongDifus*) emalloc(sizeof(LongDifus));
201  n = ml->nodecount;
202 
203  pld->dchange = 0;
204  pld->mindex = (int*) ecalloc(n, sizeof(int));
205  pld->pindex = (int*) ecalloc(n, sizeof(int));
206  pld->state = (double**) ecalloc(n, sizeof(double*));
207  pld->a = (double*) ecalloc(n, sizeof(double));
208  pld->b = (double*) ecalloc(n, sizeof(double));
209  pld->d = (double*) ecalloc(n, sizeof(double));
210  pld->rhs = (double*) ecalloc(n, sizeof(double));
211  pld->af = (double*) ecalloc(n, sizeof(double));
212  pld->bf = (double*) ecalloc(n, sizeof(double));
213  pld->vol = (double*) ecalloc(n, sizeof(double));
214  pld->dc = (double*) ecalloc(n, sizeof(double));
215 
216  /* make a map from node_index to memb_list index. -1 means no exist*/
217  map = (int*) ecalloc(vnodecount, sizeof(int));
218  omap = (int*) ecalloc(n, sizeof(int));
219  for (i = 0; i < vnodecount; ++i) {
220  map[i] = -1;
221  }
222  for (i = 0; i < n; ++i) {
223  map[ml->nodelist[i]->v_node_index] = i;
224  }
225 #if 0
226 for (i=0; i < vnodecount; ++i) {
227  printf("%d index=%d\n", i, map[i]);
228 }
229 #endif
230  /* order the indices for efficient gaussian elimination */
231  /* But watch out for 0 area nodes. Use the parent of parent */
232  /* But if parent of parent does not have diffusion mechanism
233  check the parent section */
234  /* And watch out for root. Use first node of root section */
235  for (i = 0, j = 0; i < vnodecount; ++i) {
236  if (map[i] > -1) {
237  pld->mindex[j] = map[i];
238  omap[map[i]] = j; /* from memb list index to order */
239  pnd = _nt->_v_parent[i];
240  pindex = map[pnd->v_node_index];
241  if (pindex == -1) { /* maybe this was zero area node */
242  pnd = _nt->_v_parent[pnd->v_node_index];
243  if (pnd) {
244  pindex = map[pnd->v_node_index];
245  if (pindex < 0) { /* but what about the parent section */
246  Section* psec = _nt->_v_node[i]->sec->parentsec;
247  if (psec) {
248  pnd = psec->pnode[0];
249  pindex = map[pnd->v_node_index];
250  }
251  }
252  } else { /* maybe this section is not the root */
253  Section* psec = _nt->_v_node[i]->sec->parentsec;
254  if (psec) {
255  pnd = psec->pnode[0];
256  pindex = map[pnd->v_node_index];
257  }
258  }
259  }
260  if (pindex > -1) {
261  pld->pindex[j] = omap[pindex];
262  } else {
263  pld->pindex[j] = -1;
264  }
265  ++j;
266  }
267  }
268  longdifus_diamchange(pld, m, sindex, ml, _nt);
269 #if 0
270  for (i=0; i < n; ++i) {
271 printf("i=%d pin=%d mi=%d :%s node %d state[(%i)]=%g\n", i, pld->pindex[i],
272  pld->mindex[i], secname(ml->nodelist[pld->mindex[i]]->sec),
273  ml->nodelist[pld->mindex[i]]->sec_node_index_
274  , sindex, pld->state[i][0]);
275  }
276 #endif
277  free(map);
278  free(omap);
279 }
280 
281 /* called at end of v_setup_vectors only for thread 0 */
282 /* only v makes sense and the purpose is to free the old, allocate space */
283 /* for the new, and setup the tml field */
284 /* the args used are m and v */
285 static void overall_setup(int m,
286  ldifusfunc3_t diffunc,
287  void** v,
288  int ai,
289  int sindex,
290  int dindex,
291  NrnThread* _nt) {
292  int i;
294  LongDifusThreadData* ldtd = *ppldtd;
295  if (ldtd) { /* free the whole thing */
296  free(ldtd->ml);
297  for (i = 0; i < ldtd->nthread; ++i) {
298  if (ldtd->ldifus[i]) {
299  longdifusfree(ldtd->ldifus + i);
300  }
301  }
302  free(ldtd->ldifus);
303  free(ldtd);
304  *ppldtd = (LongDifusThreadData*) 0;
305  }
306  /* new overall space */
307  *ppldtd = (LongDifusThreadData*) emalloc(sizeof(LongDifusThreadData));
308  ldtd = *ppldtd;
309  ldtd->nthread = nrn_nthread;
310  ldtd->ldifus = (LongDifus**) ecalloc(nrn_nthread, sizeof(LongDifus*));
311  ldtd->ml = (Memb_list**) ecalloc(nrn_nthread, sizeof(Memb_list*));
312  /* which have memb_list pointers */
313  for (i = 0; i < nrn_nthread; ++i) {
314  NrnThreadMembList* tml;
315  for (tml = nrn_threads[i].tml; tml; tml = tml->next) {
316  if (tml->index == m) {
317  ldtd->ml[i] = tml->ml;
318  longdifusalloc(ldtd->ldifus + i, m, sindex, tml->ml, nrn_threads + i);
319  break;
320  }
321  }
322  }
323 }
324 
325 static LongDifus* v2ld(void** v, int tid) {
327  return (*ppldtd)->ldifus[tid];
328 }
329 
330 static Memb_list* v2ml(void** v, int tid) {
332  return (*ppldtd)->ml[tid];
333 }
334 
335 static void
336 stagger(int m, ldifusfunc3_t diffunc, void** v, int ai, int sindex, int dindex, NrnThread* _nt) {
337  LongDifus* pld;
338  int i, n, di;
339  double dc, vol, dfdi, dx;
340  double** data;
341  Datum** pdata;
342  Datum* thread;
343  Memb_list* ml;
344 
345  di = dindex + ai;
346 
347  pld = v2ld(v, _nt->id);
348  if (!pld)
349  return;
350  ml = v2ml(v, _nt->id);
351 
352  n = ml->nodecount;
353  data = ml->data;
354  pdata = ml->pdata;
355  thread = ml->_thread;
356 
357  longdifus_diamchange(pld, m, sindex, ml, _nt);
358  /*flux and volume coefficients (if dc is constant this is too often)*/
359  for (i = 0; i < n; ++i) {
360  int pin = pld->pindex[i];
361  int mi = pld->mindex[i];
362  pld->dc[i] = (*diffunc)(ai, data[mi], pdata[mi], pld->vol + i, &dfdi, thread, _nt);
363  pld->d[i] = 0.;
364 #if 0
365  if (dfdi) {
366  pld->d[i] += fabs(dfdi)/pld->vol[i]/pld->state[i][ai];
367  }
368 #endif
369  if (pin > -1) {
370  /* D * area between compartments */
371  dc = (pld->dc[i] + pld->dc[pin]) / 2.;
372 
373  pld->a[i] = -pld->af[i] * dc / pld->vol[pin];
374  pld->b[i] = -pld->bf[i] * dc / pld->vol[i];
375  }
376  }
377  /* setup matrix */
378  for (i = 0; i < n; ++i) {
379  int pin = pld->pindex[i];
380  int mi = pld->mindex[i];
381  pld->d[i] += 1. / nt_dt;
382  pld->rhs[i] = pld->state[i][ai] / nt_dt;
383  if (pin > -1) {
384  pld->d[i] -= pld->b[i];
385  pld->d[pin] -= pld->a[i];
386  }
387  }
388 #if 0
389 for (i=0; i < n; ++i) { double a,b;
390  if (pld->pindex[i] > -1) {
391  a = pld->a[i];
392  b = pld->b[i];
393  }else{ a=b=0.;}
394  printf("i=%d a=%g b=%g d=%g rhs=%g state=%g\n",
395  i, a, b, pld->d[i], pld->rhs[i], pld->state[i][ai]);
396 }
397 #endif
398 
399  /* we've set up the matrix; now solve it */
400  nrn_tree_solve(pld->a, pld->d, pld->b, pld->rhs, pld->pindex, n);
401 
402  /* update answer */
403  for (i = 0; i < n; ++i) {
404  pld->state[i][ai] = pld->rhs[i];
405  }
406 }
407 
408 static void
409 ode(int m, ldifusfunc3_t diffunc, void** v, int ai, int sindex, int dindex, NrnThread* _nt) {
410  LongDifus* pld;
411  int i, n, di;
412  double dc, vol, dfdi;
413  double** data;
414  Datum** pdata;
415  Datum* thread;
416  Memb_list* ml;
417 
418  di = dindex + ai;
419 
420  pld = v2ld(v, _nt->id);
421  if (!pld)
422  return;
423  ml = v2ml(v, _nt->id);
424 
425  n = ml->nodecount;
426  data = ml->data;
427  pdata = ml->pdata;
428  thread = ml->_thread;
429 
430  longdifus_diamchange(pld, m, sindex, ml, _nt);
431  /*flux and volume coefficients (if dc is constant this is too often)*/
432  for (i = 0; i < n; ++i) {
433  int pin = pld->pindex[i];
434  int mi = pld->mindex[i];
435  pld->dc[i] = (*diffunc)(ai, data[mi], pdata[mi], pld->vol + i, &dfdi, thread, _nt);
436  if (pin > -1) {
437  /* D * area between compartments */
438  dc = (pld->dc[i] + pld->dc[pin]) / 2.;
439 
440  pld->a[i] = pld->af[i] * dc / pld->vol[pin];
441  pld->b[i] = pld->bf[i] * dc / pld->vol[i];
442  }
443  }
444  /* add terms to diffeq */
445  for (i = 0; i < n; ++i) {
446  double dif;
447  int pin = pld->pindex[i];
448  int mi = pld->mindex[i];
449 #if 0
450  pld->d[i] = data[mi][di];
451 #endif
452  if (pin > -1) {
453  dif = (pld->state[pin][ai] - pld->state[i][ai]);
454  data[mi][di] += dif * pld->b[i];
455  data[pld->mindex[pin]][di] -= dif * pld->a[i];
456  }
457  }
458 #if 0
459  for (i=0; i < n; ++i) {
460  int mi = pld->mindex[i];
461  printf("%d olddstate=%g new=%g\n", i, pld->d[i], data[mi][di]);
462  }
463 #endif
464 }
465 
466 
467 static void
468 matsol(int m, ldifusfunc3_t diffunc, void** v, int ai, int sindex, int dindex, NrnThread* _nt) {
469  LongDifus* pld;
470  int i, n, di;
471  double dc, vol, dfdi;
472  double** data;
473  Datum** pdata;
474  Datum* thread;
475  Memb_list* ml;
476 
477  di = dindex + ai;
478 
479  pld = v2ld(v, _nt->id);
480  if (!pld)
481  return;
482  ml = v2ml(v, _nt->id);
483 
484  n = ml->nodecount;
485  data = ml->data;
486  pdata = ml->pdata;
487  thread = ml->_thread;
488 
489  /*flux and volume coefficients (if dc is constant this is too often)*/
490  for (i = 0; i < n; ++i) {
491  int pin = pld->pindex[i];
492  int mi = pld->mindex[i];
493  pld->dc[i] = (*diffunc)(ai, data[mi], pdata[mi], pld->vol + i, &dfdi, thread, _nt);
494  pld->d[i] = 0.;
495  if (dfdi) {
496  pld->d[i] += fabs(dfdi) / pld->vol[i] / pld->state[i][ai];
497 #if 0
498 printf("i=%d state=%g vol=%g dfdc=%g\n", i, pld->state[i][ai],pld->vol[i], pld->d[i]);
499 #endif
500  }
501  if (pin > -1) {
502  /* D * area between compartments */
503  dc = (pld->dc[i] + pld->dc[pin]) / 2.;
504 
505  pld->a[i] = -pld->af[i] * dc / pld->vol[pin];
506  pld->b[i] = -pld->bf[i] * dc / pld->vol[i];
507  }
508  }
509  /* setup matrix */
510  for (i = 0; i < n; ++i) {
511  int pin = pld->pindex[i];
512  int mi = pld->mindex[i];
513  pld->d[i] += 1. / nt_dt;
514  pld->rhs[i] = data[mi][di] / nt_dt;
515  if (pin > -1) {
516  pld->d[i] -= pld->b[i];
517  pld->d[pin] -= pld->a[i];
518  }
519  }
520 #if 0
521 for (i=0; i < n; ++i) { double a,b;
522  int mi = pld->mindex[i];
523  if (pld->pindex[i] > -1) {
524  a = pld->a[i];
525  b = pld->b[i];
526  }else{ a=b=0.;}
527  printf("i=%d a=%g b=%g d=%g rhs=%g dstate=%g\n",
528  i, a, b, pld->d[i], pld->rhs[i], data[mi][di]);
529 }
530 #endif
531  /* triang */
532  for (i = n - 1; i > 0; --i) {
533  int pin = pld->pindex[i];
534  if (pin > -1) {
535  double p;
536  p = pld->a[i] / pld->d[i];
537  pld->d[pin] -= p * pld->b[i];
538  pld->rhs[pin] -= p * pld->rhs[i];
539  }
540  }
541  /* bksub */
542  for (i = 0; i < n; ++i) {
543  int pin = pld->pindex[i];
544  if (pin > -1) {
545  pld->rhs[i] -= pld->b[i] * pld->rhs[pin];
546  }
547  pld->rhs[i] /= pld->d[i];
548  }
549  /* update answer */
550  for (i = 0; i < n; ++i) {
551  int mi = pld->mindex[i];
552  data[mi][di] = pld->rhs[i];
553  }
554 }
const char * secname(Section *sec)
Definition: cabcode.cpp:1776
double section_length(Section *sec)
Definition: cabcode.cpp:387
short index
Definition: cabvars.h:10
static Node * pnd
Definition: clamp.cpp:33
#define dc
#define assert(ex)
Definition: hocassrt.h:32
void * erealloc(void *ptr, size_t n)
Definition: symbol.cpp:263
void * ecalloc(size_t n, size_t size)
Definition: symbol.cpp:215
struct LongDifusThreadData LongDifusThreadData
static int ldifusfunccnt
Definition: ldifus.cpp:41
#define nt_dt
Definition: ldifus.cpp:14
static void longdifusalloc(LongDifus **ppld, int m, int sindex, Memb_list *ml, NrnThread *_nt)
Definition: ldifus.cpp:192
static ldifusfunc2_t stagger
Definition: ldifus.cpp:44
void long_difus_solve(int method, NrnThread *nt)
Definition: ldifus.cpp:104
static ldifusfunc2_t ode
Definition: ldifus.cpp:44
void nrn_tree_solve(double *a, double *d, double *b, double *rhs, int *pindex, int n)
Definition: ldifus.cpp:69
int structure_change_cnt
Definition: ldifus.cpp:17
int diam_change_cnt
Definition: ldifus.cpp:16
static Memb_list * v2ml(void **v, int tid)
Definition: ldifus.cpp:330
static void longdifusfree(LongDifus **ppld)
Definition: ldifus.cpp:130
static void longdifus_diamchange(LongDifus *pld, int m, int sindex, Memb_list *ml, NrnThread *_nt)
Definition: ldifus.cpp:152
static ldifusfunc2_t matsol
Definition: ldifus.cpp:44
static ldifusfunc2_t overall_setup
Definition: ldifus.cpp:44
void hoc_register_ldifus1(ldifusfunc_t f)
Definition: ldifus.cpp:46
static ldifusfunc_t * ldifusfunc
Definition: ldifus.cpp:42
struct LongDifus LongDifus
static LongDifus * v2ld(void **v, int tid)
Definition: ldifus.cpp:325
#define rhs
Definition: lineq.h:6
#define v
Definition: md1redef.h:4
#define i
Definition: md1redef.h:12
#define pdata
Definition: md1redef.h:28
static double map(void *v)
Definition: mlinedit.cpp:47
fabs
Definition: extdef.h:3
char * emalloc(unsigned n)
Definition: list.cpp:166
int nrn_nthread
Definition: multicore.cpp:46
NrnThread * nrn_threads
Definition: multicore.cpp:47
#define printf
Definition: mwprefix.h:26
int const size_t const size_t n
Definition: nrngsl.h:11
size_t p
size_t j
double(* ldifusfunc3_t)(int, double *, Datum *, double *, double *, Datum *, NrnThread *)
Definition: nrniv_mf.h:11
void ldifusfunc2_t(int, ldifusfunc3_t, void **, int, int, int, NrnThread *)
Definition: nrniv_mf.h:12
void(* ldifusfunc_t)(ldifusfunc2_t, NrnThread *)
Definition: nrniv_mf.h:13
#define data
Definition: rbtqueue.cpp:49
#define NULL
Definition: sptree.h:16
double * dc
Definition: ldifus.cpp:31
double * af
Definition: ldifus.cpp:28
int dchange
Definition: ldifus.cpp:20
int * mindex
Definition: ldifus.cpp:21
double * rhs
Definition: ldifus.cpp:27
double * d
Definition: ldifus.cpp:26
double * vol
Definition: ldifus.cpp:30
double * b
Definition: ldifus.cpp:25
double ** state
Definition: ldifus.cpp:23
int * pindex
Definition: ldifus.cpp:22
double * a
Definition: ldifus.cpp:24
double * bf
Definition: ldifus.cpp:29
Memb_list ** ml
Definition: ldifus.cpp:38
LongDifus ** ldifus
Definition: ldifus.cpp:37
int nodecount
Definition: nrnoc_ml.h:18
Node ** nodelist
Definition: nrnoc_ml.h:5
double ** data
Definition: nrnoc_ml.h:14
Datum ** pdata
Definition: nrnoc_ml.h:15
Datum * _thread
Definition: nrnoc_ml.h:17
Definition: section.h:133
Section * sec
Definition: section.h:155
int v_node_index
Definition: section.h:175
int sec_node_index_
Definition: section.h:177
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int id
Definition: multicore.h:66
int end
Definition: multicore.h:65
Node ** _v_parent
Definition: multicore.h:78
Node ** _v_node
Definition: multicore.h:77
struct NrnThreadMembList * next
Definition: multicore.h:34
Memb_list * ml
Definition: multicore.h:35
Datum * dparam
Definition: section.h:220
struct Node ** pnode
Definition: section.h:51
struct Prop * prop
Definition: section.h:63
struct Section * parentsec
Definition: section.h:42
short nnode
Definition: section.h:41
Definition: hocdec.h:177
double * pval
Definition: hocdec.h:181
double val
Definition: hocdec.h:178