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 struct LongDifus {
17  int dchange{};
18  int* mindex; /* index into memb_list[m] */
19  int* pindex; /* parent in this struct */
20  std::vector<neuron::container::data_handle<double>> state;
21  double* a;
22  double* b;
23  double* d;
24  double* rhs;
25  double* af; /* efficiency benefit is very low, rall/dx^2 */
26  double* bf; /* 1/dx^2 */
27  double* vol; /* volatile volume from COMPARTMENT */
28  double* dc; /* volatile diffusion constant * cross sectional
29  area from LONGITUDINAL_DIFFUSION */
30  LongDifus(std::size_t n)
31  : state(n) {}
32 };
33 
35  int nthread;
38 };
39 
40 static int ldifusfunccnt;
42 
44 
48  ++ldifusfunccnt;
49 }
50 
51 extern "C" void nrn_tree_solve(double* a, double* d, double* b, double* rhs, int* pindex, int n) {
52  /*
53  treesolver
54 
55  a - above the diagonal
56  d - diagonal
57  b - below the diagonal
58  rhs - right hand side, which is changed to the result
59  pindex - parent indices
60  n - number of states
61  */
62 
63  int i;
64 
65  /* triang */
66  for (i = n - 1; i > 0; --i) {
67  int pin = pindex[i];
68  if (pin > -1) {
69  double p;
70  p = a[i] / d[i];
71  d[pin] -= p * b[i];
72  rhs[pin] -= p * rhs[i];
73  }
74  }
75  /* bksub */
76  for (i = 0; i < n; ++i) {
77  int pin = pindex[i];
78  if (pin > -1) {
79  rhs[i] -= b[i] * rhs[pin];
80  }
81  rhs[i] /= d[i];
82  }
83 }
84 
85 
86 void long_difus_solve(neuron::model_sorted_token const& sorted_token, int method, NrnThread& nt) {
87  ldifusfunc2_t* f{};
88  if (ldifusfunc) {
89  switch (method) {
90  case 0: /* normal staggered time step */
91  f = stagger;
92  break;
93  case 1: /* dstate = f(state) */
94  f = ode;
95  break;
96  case 2: /* solve (1 + dt*jacobian)*x = b */
97  f = matsol;
98  break;
99  case 3: /* setup only called by thread 0 */
100  f = overall_setup;
101  break;
102  }
103  assert(f);
104  for (int i = 0; i < ldifusfunccnt; ++i) {
105  ldifusfunc[i](f, sorted_token, nt);
106  }
107  }
108 }
109 
110 static void longdifusfree(LongDifus** ppld) {
111  if (*ppld) {
112  LongDifus* pld = *ppld;
113  free(pld->mindex);
114  free(pld->pindex);
115  free(pld->a);
116  free(pld->b);
117  free(pld->d);
118  free(pld->rhs);
119  free(pld->af);
120  free(pld->bf);
121  free(pld->vol);
122  free(pld->dc);
123  }
124  delete std::exchange(*ppld, nullptr);
125 }
126 
127 // sindex is a non-legacy field index
128 static void longdifus_diamchange(LongDifus* pld, int m, int sindex, Memb_list* ml, NrnThread* _nt) {
129  int i, n, mi, mpi, pindex;
130  Node *nd, *pnd;
131  double rall, dxp, dxc;
132 
133  if (pld->dchange == diam_change_cnt) {
134  return;
135  }
136  /*printf("longdifus_diamchange %d %d\n", pld->dchange, diam_change_cnt);*/
137  n = ml->nodecount;
138  for (i = 0; i < n; ++i) {
139  /* For every child with a parent having this mechanism */
140  /* Also child may butte end to end with parent or attach to middle */
141  mi = pld->mindex[i];
142  if (sindex < 0) {
143  pld->state[i] = static_cast<neuron::container::data_handle<double>>(
144  ml->pdata[mi][-sindex - 1].get<double*>());
145  } else {
146  // if this is an array variable then take a handle to the zeroth entry of it
147  pld->state[i] = ml->data_handle(mi, {sindex, 0});
148  }
149  nd = ml->nodelist[mi];
150  pindex = pld->pindex[i];
151  if (pindex > -1) {
152  mpi = pld->mindex[pindex];
153  pnd = ml->nodelist[mpi];
154  if (nd->sec_node_index_ == 0) {
155  rall = nd->sec->prop->dparam[4].get<double>();
156  } else {
157  rall = 1.;
158  }
159  dxc = section_length(nd->sec) / ((double) (nd->sec->nnode - 1));
160  dxp = section_length(pnd->sec) / ((double) (pnd->sec->nnode - 1));
161  pld->af[i] = 2 * rall / dxp / (dxc + dxp);
162  pld->bf[i] = 2 / dxc / (dxc + dxp);
163  }
164  }
165  pld->dchange = diam_change_cnt;
166 }
167 
168 static void longdifusalloc(LongDifus** ppld, int m, int sindex, Memb_list* ml, NrnThread* _nt) {
169  auto const n = ml->nodecount;
170  assert(n > 0);
171  auto* const pld = new LongDifus{static_cast<std::size_t>(n)};
172  *ppld = pld;
173  pld->mindex = (int*) ecalloc(n, sizeof(int));
174  pld->pindex = (int*) ecalloc(n, sizeof(int));
175  pld->a = (double*) ecalloc(n, sizeof(double));
176  pld->b = (double*) ecalloc(n, sizeof(double));
177  pld->d = (double*) ecalloc(n, sizeof(double));
178  pld->rhs = (double*) ecalloc(n, sizeof(double));
179  pld->af = (double*) ecalloc(n, sizeof(double));
180  pld->bf = (double*) ecalloc(n, sizeof(double));
181  pld->vol = (double*) ecalloc(n, sizeof(double));
182  pld->dc = (double*) ecalloc(n, sizeof(double));
183 
184  /* make a map from node_index to memb_list index. -1 means no exist*/
185  auto const vnodecount = _nt->end;
186  std::vector<int> map(vnodecount, -1), omap(n);
187  for (int i = 0; i < n; ++i) {
188  map[ml->nodelist[i]->v_node_index] = i;
189  }
190  /* order the indices for efficient gaussian elimination */
191  /* But watch out for 0 area nodes. Use the parent of parent */
192  /* But if parent of parent does not have diffusion mechanism
193  check the parent section */
194  /* And watch out for root. Use first node of root section */
195  for (int i = 0, j = 0; i < vnodecount; ++i) {
196  if (map[i] > -1) {
197  pld->mindex[j] = map[i];
198  omap[map[i]] = j; /* from memb list index to order */
199  Node* pnd = _nt->_v_parent[i];
200  auto pindex = map[pnd->v_node_index];
201  if (pindex == -1) { /* maybe this was zero area node */
202  pnd = _nt->_v_parent[pnd->v_node_index];
203  if (pnd) {
204  pindex = map[pnd->v_node_index];
205  if (pindex < 0) { /* but what about the parent section */
206  Section* psec = _nt->_v_node[i]->sec->parentsec;
207  if (psec) {
208  pnd = psec->pnode[0];
209  pindex = map[pnd->v_node_index];
210  }
211  }
212  } else { /* maybe this section is not the root */
213  Section* psec = _nt->_v_node[i]->sec->parentsec;
214  if (psec) {
215  pnd = psec->pnode[0];
216  pindex = map[pnd->v_node_index];
217  }
218  }
219  }
220  if (pindex > -1) {
221  pld->pindex[j] = omap[pindex];
222  } else {
223  pld->pindex[j] = -1;
224  }
225  ++j;
226  }
227  }
228  longdifus_diamchange(pld, m, sindex, ml, _nt);
229 }
230 
231 /* called at end of v_setup_vectors only for thread 0 */
232 /* only v makes sense and the purpose is to free the old, allocate space */
233 /* for the new, and setup the tml field */
234 /* the args used are m and v */
235 static void overall_setup(int m,
236  ldifusfunc3_t diffunc,
237  void** v,
238  int ai,
239  int sindex,
240  int dindex,
242  NrnThread& ntr) {
243  int i;
245  LongDifusThreadData* ldtd = *ppldtd;
246  if (ldtd) { /* free the whole thing */
247  free(ldtd->ml);
248  for (i = 0; i < ldtd->nthread; ++i) {
249  if (ldtd->ldifus[i]) {
250  longdifusfree(ldtd->ldifus + i);
251  }
252  }
253  free(ldtd->ldifus);
254  free(ldtd);
255  *ppldtd = (LongDifusThreadData*) 0;
256  }
257  /* new overall space */
258  *ppldtd = (LongDifusThreadData*) emalloc(sizeof(LongDifusThreadData));
259  ldtd = *ppldtd;
260  ldtd->nthread = nrn_nthread;
261  ldtd->ldifus = (LongDifus**) ecalloc(nrn_nthread, sizeof(LongDifus*));
262  ldtd->ml = (Memb_list**) ecalloc(nrn_nthread, sizeof(Memb_list*));
263  /* which have memb_list pointers */
264  for (i = 0; i < nrn_nthread; ++i) {
265  NrnThreadMembList* tml;
266  for (tml = nrn_threads[i].tml; tml; tml = tml->next) {
267  if (tml->index == m) {
268  ldtd->ml[i] = tml->ml;
269  longdifusalloc(ldtd->ldifus + i, m, sindex, tml->ml, nrn_threads + i);
270  break;
271  }
272  }
273  }
274 }
275 
276 static LongDifus* v2ld(void** v, int tid) {
278  return (*ppldtd)->ldifus[tid];
279 }
280 
281 static Memb_list* v2ml(void** v, int tid) {
283  return (*ppldtd)->ml[tid];
284 }
285 
286 static void stagger(int m,
287  ldifusfunc3_t diffunc,
288  void** v,
289  int ai, // array index
290  int sindex, // field index of {x} variable
291  int dindex, // field index of D{x} variable
292  neuron::model_sorted_token const& sorted_token,
293  NrnThread& ntr) {
294  auto* const _nt = &ntr;
295  LongDifus* const pld = v2ld(v, _nt->id);
296  if (!pld) {
297  return;
298  }
299  auto* const ml = v2ml(v, _nt->id);
300  int const n = ml->nodecount;
301  Datum** const pdata = ml->pdata;
302  Datum* const thread = ml->_thread;
303 
304  longdifus_diamchange(pld, m, sindex, ml, _nt);
305  /*flux and volume coefficients (if dc is constant this is too often)*/
306  for (int i = 0; i < n; ++i) {
307  int pin = pld->pindex[i];
308  int mi = pld->mindex[i];
309  double dfdi;
310  pld->dc[i] = diffunc(ai, ml, mi, pdata[mi], pld->vol + i, &dfdi, thread, _nt, sorted_token);
311  pld->d[i] = 0.;
312  if (pin > -1) {
313  /* D * area between compartments */
314  double const dc = (pld->dc[i] + pld->dc[pin]) / 2.;
315  pld->a[i] = -pld->af[i] * dc / pld->vol[pin];
316  pld->b[i] = -pld->bf[i] * dc / pld->vol[i];
317  }
318  }
319  /* setup matrix */
320  for (int i = 0; i < n; ++i) {
321  int pin = pld->pindex[i];
322  pld->d[i] += 1. / nt_dt;
323  pld->rhs[i] = *(pld->state[i].next_array_element(ai)) / nt_dt;
324  if (pin > -1) {
325  pld->d[i] -= pld->b[i];
326  pld->d[pin] -= pld->a[i];
327  }
328  }
329 
330  /* we've set up the matrix; now solve it */
331  nrn_tree_solve(pld->a, pld->d, pld->b, pld->rhs, pld->pindex, n);
332 
333  /* update answer */
334  for (int i = 0; i < n; ++i) {
335  *(pld->state[i].next_array_element(ai)) = pld->rhs[i];
336  }
337 }
338 
339 static void ode(int m,
340  ldifusfunc3_t diffunc,
341  void** v,
342  int ai, // array index
343  int sindex, // field index of {x} variable
344  int dindex, // field index of D{x} variable
345  neuron::model_sorted_token const& sorted_token,
346  NrnThread& ntr) {
347  auto* const _nt = &ntr;
348  LongDifus* const pld = v2ld(v, _nt->id);
349  if (!pld) {
350  return;
351  }
352  auto* const ml = v2ml(v, _nt->id);
353  int const n = ml->nodecount;
354  Datum** const pdata = ml->pdata;
355  Datum* const thread = ml->_thread;
356  longdifus_diamchange(pld, m, sindex, ml, _nt);
357  /*flux and volume coefficients (if dc is constant this is too often)*/
358  for (int i = 0; i < n; ++i) {
359  int pin = pld->pindex[i];
360  int mi = pld->mindex[i];
361  double dfdi;
362  pld->dc[i] = diffunc(ai, ml, mi, pdata[mi], pld->vol + i, &dfdi, thread, _nt, sorted_token);
363  if (pin > -1) {
364  /* D * area between compartments */
365  double const dc = (pld->dc[i] + pld->dc[pin]) / 2.;
366  pld->a[i] = pld->af[i] * dc / pld->vol[pin];
367  pld->b[i] = pld->bf[i] * dc / pld->vol[i];
368  }
369  }
370  /* add terms to diffeq */
371  for (int i = 0; i < n; ++i) {
372  double dif;
373  int pin = pld->pindex[i];
374  int mi = pld->mindex[i];
375  if (pin > -1) {
376  dif = *(pld->state[pin].next_array_element(ai)) -
377  *(pld->state[i].next_array_element(ai));
378  ml->data(mi, dindex, ai) += dif * pld->b[i];
379  ml->data(pld->mindex[pin], dindex, ai) -= dif * pld->a[i];
380  }
381  }
382 }
383 
384 static void matsol(int m,
385  ldifusfunc3_t diffunc,
386  void** v,
387  int ai, // array index
388  int sindex, // field index of {x} variable
389  int dindex, // field index of D{x} variable
390  neuron::model_sorted_token const& sorted_token,
391  NrnThread& ntr) {
392  auto* const _nt = &ntr;
393  LongDifus* const pld = v2ld(v, _nt->id);
394  if (!pld) {
395  return;
396  }
397  auto* const ml = v2ml(v, _nt->id);
398  int const n = ml->nodecount;
399  Datum** const pdata = ml->pdata;
400  Datum* const thread = ml->_thread;
401 
402  /*flux and volume coefficients (if dc is constant this is too often)*/
403  for (int i = 0; i < n; ++i) {
404  int pin = pld->pindex[i];
405  int mi = pld->mindex[i];
406  double dfdi;
407  pld->dc[i] = diffunc(ai, ml, mi, pdata[mi], pld->vol + i, &dfdi, thread, _nt, sorted_token);
408  pld->d[i] = 0.;
409  if (dfdi) {
410  pld->d[i] += fabs(dfdi) / pld->vol[i] / *(pld->state[i].next_array_element(ai));
411  }
412  if (pin > -1) {
413  /* D * area between compartments */
414  auto const dc = (pld->dc[i] + pld->dc[pin]) / 2.;
415  pld->a[i] = -pld->af[i] * dc / pld->vol[pin];
416  pld->b[i] = -pld->bf[i] * dc / pld->vol[i];
417  }
418  }
419  /* setup matrix */
420  for (int i = 0; i < n; ++i) {
421  int pin = pld->pindex[i];
422  int mi = pld->mindex[i];
423  pld->d[i] += 1. / nt_dt;
424  pld->rhs[i] = ml->data(mi, dindex, ai) / nt_dt;
425  if (pin > -1) {
426  pld->d[i] -= pld->b[i];
427  pld->d[pin] -= pld->a[i];
428  }
429  }
430  /* triang */
431  for (int i = n - 1; i > 0; --i) {
432  int pin = pld->pindex[i];
433  if (pin > -1) {
434  double p;
435  p = pld->a[i] / pld->d[i];
436  pld->d[pin] -= p * pld->b[i];
437  pld->rhs[pin] -= p * pld->rhs[i];
438  }
439  }
440  /* bksub */
441  for (int i = 0; i < n; ++i) {
442  int pin = pld->pindex[i];
443  if (pin > -1) {
444  pld->rhs[i] -= pld->b[i] * pld->rhs[pin];
445  }
446  pld->rhs[i] /= pld->d[i];
447  }
448  /* update answer */
449  for (int i = 0; i < n; ++i) {
450  int mi = pld->mindex[i];
451  ml->data(mi, dindex, ai) = pld->rhs[i];
452  }
453 }
double section_length(Section *sec)
Definition: cabcode.cpp:401
static Node * pnd
Definition: clamp.cpp:33
#define v
Definition: md1redef.h:11
#define i
Definition: md1redef.h:19
#define pdata
Definition: md1redef.h:37
#define assert(ex)
Definition: hocassrt.h:24
static int ldifusfunccnt
Definition: ldifus.cpp:40
#define nt_dt
Definition: ldifus.cpp:14
static void longdifusalloc(LongDifus **ppld, int m, int sindex, Memb_list *ml, NrnThread *_nt)
Definition: ldifus.cpp:168
static ldifusfunc2_t stagger
Definition: ldifus.cpp:43
static ldifusfunc2_t ode
Definition: ldifus.cpp:43
void nrn_tree_solve(double *a, double *d, double *b, double *rhs, int *pindex, int n)
Definition: ldifus.cpp:51
static Memb_list * v2ml(void **v, int tid)
Definition: ldifus.cpp:281
static void longdifusfree(LongDifus **ppld)
Definition: ldifus.cpp:110
static void longdifus_diamchange(LongDifus *pld, int m, int sindex, Memb_list *ml, NrnThread *_nt)
Definition: ldifus.cpp:128
void long_difus_solve(neuron::model_sorted_token const &sorted_token, int method, NrnThread &nt)
Definition: ldifus.cpp:86
static ldifusfunc2_t matsol
Definition: ldifus.cpp:43
static ldifusfunc2_t overall_setup
Definition: ldifus.cpp:43
void hoc_register_ldifus1(ldifusfunc_t f)
Definition: ldifus.cpp:45
static ldifusfunc_t * ldifusfunc
Definition: ldifus.cpp:41
static LongDifus * v2ld(void **v, int tid)
Definition: ldifus.cpp:276
#define rhs
Definition: lineq.h:6
static double map(void *v)
Definition: mlinedit.cpp:43
fabs
Definition: extdef.h:3
NrnThread * nrn_threads
Definition: multicore.cpp:56
int nrn_nthread
Definition: multicore.cpp:55
void * erealloc(void *ptr, size_t size)
Definition: nrnoc_aux.cpp:94
void * ecalloc(size_t n, size_t size)
Definition: nrnoc_aux.cpp:85
static void * emalloc(size_t size)
Definition: mpispike.cpp:30
int const size_t const size_t n
Definition: nrngsl.h:10
size_t p
size_t j
void(*)(ldifusfunc2_t, neuron::model_sorted_token const &, NrnThread &) ldifusfunc_t
Definition: nrniv_mf.h:21
int diam_change_cnt
Definition: treeset.cpp:67
void(int, ldifusfunc3_t, void **, int, int, int, neuron::model_sorted_token const &, NrnThread &) ldifusfunc2_t
Definition: nrniv_mf.h:20
double(*)(int, Memb_list *, std::size_t, Datum *, double *, double *, Datum *, NrnThread *, neuron::model_sorted_token const &) ldifusfunc3_t
Definition: nrniv_mf.h:18
std::vector< neuron::container::data_handle< double > > state
Definition: ldifus.cpp:20
double * dc
Definition: ldifus.cpp:28
double * af
Definition: ldifus.cpp:25
int dchange
Definition: ldifus.cpp:17
int * mindex
Definition: ldifus.cpp:18
double * rhs
Definition: ldifus.cpp:24
double * d
Definition: ldifus.cpp:23
double * vol
Definition: ldifus.cpp:27
LongDifus(std::size_t n)
Definition: ldifus.cpp:30
double * b
Definition: ldifus.cpp:22
int * pindex
Definition: ldifus.cpp:19
double * a
Definition: ldifus.cpp:21
double * bf
Definition: ldifus.cpp:26
Memb_list ** ml
Definition: ldifus.cpp:37
LongDifus ** ldifus
Definition: ldifus.cpp:36
A view into a set of mechanism instances.
Definition: nrnoc_ml.h:34
neuron::container::data_handle< double > data_handle(std::size_t instance, neuron::container::field_index field) const
Definition: memblist.cpp:76
int nodecount
Definition: nrnoc_ml.h:78
Node ** nodelist
Definition: nrnoc_ml.h:68
Datum ** pdata
Definition: nrnoc_ml.h:75
Definition: section.h:105
Section * sec
Definition: section.h:193
int v_node_index
Definition: section.h:212
int sec_node_index_
Definition: section.h:213
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int end
Definition: multicore.h:65
Node ** _v_parent
Definition: multicore.h:91
Node ** _v_node
Definition: multicore.h:90
struct NrnThreadMembList * next
Definition: multicore.h:34
Memb_list * ml
Definition: multicore.h:35
Datum * dparam
Definition: section.h:247
Prop * prop
Definition: section.h:71
short nnode
Definition: section.h:52
Section * parentsec
Definition: section.h:53
Node ** pnode
Definition: section.h:59
Non-template stable handle to a generic value.
T get() const
Explicit conversion to any T.