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