NEURON
rxd.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include <stdio.h>
3 #include <assert.h>
4 #include <string.h>
5 #include "grids.h"
6 #include <cfloat>
7 #include "rxd.h"
8 #include <../nrnoc/section.h>
9 #include <../nrnoc/nrn_ansi.h>
10 #include <../nrnoc/multicore.h>
11 #include "nrnwrap_Python.h"
12 #include "nrnpython.h"
13 
14 #include <thread>
15 #include <vector>
16 #include "ocmatrix.h"
17 #include "ivocvect.h"
18 
19 #include <nanobind/nanobind.h>
20 
21 namespace nb = nanobind;
22 
23 static void ode_solve(double, double*, double*);
24 extern PyTypeObject* hocobject_type;
25 extern int structure_change_cnt;
26 extern int states_cvode_offset;
28 unsigned char initialized = FALSE;
29 
30 /*
31  Globals
32 */
33 extern NrnThread* nrn_threads;
34 int NUM_THREADS = 1;
35 namespace nrn {
36 namespace rxd {
37 std::vector<std::thread> Threads;
39 } // namespace rxd
40 } // namespace nrn
42 using namespace nrn::rxd;
43 
44 extern double* dt_ptr;
45 extern double* t_ptr;
46 
47 
50 
51 /*intracellular diffusion*/
52 unsigned char diffusion = FALSE;
58 double* _rxd_a = NULL;
59 double* _rxd_b = NULL;
60 double* _rxd_c = NULL;
61 double* _rxd_d = NULL;
62 long* _rxd_p = NULL;
63 unsigned int* _rxd_zvi_child_count = NULL;
65 static int _cvode_offset;
66 static int _ecs_count;
67 
68 /*intracellular reactions*/
70 
71 /*Indices used to set atol scale*/
73 
74 /*intracellular reactions*/
75 double* states;
76 unsigned int num_states = 0;
80 double* _curr_scales = NULL;
81 std::vector<neuron::container::data_handle<double>> _conc_ptrs, _curr_ptrs;
84 
85 /*membrane fluxes*/
86 int _memb_curr_total = 0; /*number of membrane currents (sum of
87  _memb_species_count)*/
88 int _memb_curr_nodes = 0; /*corresponding number of nodes
89  equal to _memb_curr_total if Extracellular is not used*/
90 int _memb_count = 0; /*number of membrane currents*/
91 double* _rxd_flux_scale; /*array length _memb_count to scale fluxes*/
92 double* _rxd_induced_currents_scale; /*array of Extracellular current scales*/
93 int* _cur_node_indices; /*array length _memb_count into nodes index*/
94 
95 
96 int* _memb_species_count; /*array of length _memb_count
97  number of species involved in each membrane
98  current*/
99 
100 /*arrays of size _memb_count by _memb_species_count*/
101 std::vector<std::vector<neuron::container::data_handle<double>>> _memb_cur_ptrs;
103 int*** _memb_cur_mapped; /*array of pairs of indices*/
104 int*** _memb_cur_mapped_ecs; /*array of pointer into ECS grids*/
105 
106 double* _rxd_induced_currents = NULL; /*set when calculating reactions*/
108 
109 unsigned char _membrane_flux = FALSE; /*TRUE if any membrane fluxes are in the model*/
110 int* _membrane_lookup; /*states index -> position in _rxd_induced_currents*/
111 
116 
117 static void free_zvi_child() {
118  int i;
119  /*Clear previous _rxd_zvi_child*/
121  for (i = 0; i < _rxd_num_zvi; i++)
122  if (_rxd_zvi_child_count[i] > 0)
123  free(_rxd_zvi_child[i]);
124  free(_rxd_zvi_child);
125  free(_rxd_zvi_child_count);
128  }
129 }
130 
131 static void transfer_to_legacy() {
132  /*TODO: support 3D*/
133  int i;
134  for (i = 0; i < _conc_count; i++) {
136  }
137 }
138 
139 static inline void* allocopy(void* src, size_t size) {
140  void* dst = malloc(size);
141  memcpy(dst, src, size);
142  return dst;
143 }
144 
145 extern "C" NRN_EXPORT void rxd_set_no_diffusion() {
146  diffusion = FALSE;
147  if (_rxd_a != NULL) {
148  free(_rxd_a);
149  free(_rxd_b);
150  free(_rxd_c);
151  free(_rxd_d);
152  free(_rxd_p);
153  free(_rxd_euler_nonzero_i);
154  free(_rxd_euler_nonzero_j);
156  _rxd_a = NULL;
157  }
158 }
159 
160 extern "C" NRN_EXPORT void free_curr_ptrs() {
161  _curr_count = 0;
162  if (_curr_indices != NULL)
163  free(_curr_indices);
165  if (_curr_scales != NULL)
166  free(_curr_scales);
167  _curr_scales = NULL;
168  _curr_ptrs.clear();
169 }
170 
171 extern "C" NRN_EXPORT void free_conc_ptrs() {
172  _conc_count = 0;
173  if (_conc_indices != NULL)
174  free(_conc_indices);
176  _conc_ptrs.clear();
177 }
178 
179 
180 extern "C" NRN_EXPORT void rxd_setup_curr_ptrs(int num_currents,
181  int* curr_index,
182  double* curr_scale,
183  PyHocObject** curr_ptrs) {
184  free_curr_ptrs();
185  /* info for NEURON currents - to update states */
186  _curr_count = num_currents;
187  _curr_indices = (int*) malloc(sizeof(int) * num_currents);
188  memcpy(_curr_indices, curr_index, sizeof(int) * num_currents);
189 
190  _curr_scales = (double*) malloc(sizeof(double) * num_currents);
191  memcpy(_curr_scales, curr_scale, sizeof(double) * num_currents);
192 
193  _curr_ptrs.resize(num_currents);
194  for (int i = 0; i < num_currents; i++)
195  _curr_ptrs[i] = curr_ptrs[i]->u.px_;
196 }
197 
198 extern "C" NRN_EXPORT void rxd_setup_conc_ptrs(int conc_count,
199  int* conc_index,
200  PyHocObject** conc_ptrs) {
201  /* info for NEURON concentration - to transfer to legacy */
202  int i;
203  free_conc_ptrs();
204  _conc_count = conc_count;
205  _conc_indices = (int*) malloc(sizeof(int) * conc_count);
206  memcpy(_conc_indices, conc_index, sizeof(int) * conc_count);
207  _conc_ptrs.resize(conc_count);
208  for (i = 0; i < conc_count; i++)
209  _conc_ptrs[i] = conc_ptrs[i]->u.px_;
210 }
211 
212 extern "C" NRN_EXPORT void rxd_include_node_flux3D(int grid_count,
213  int* grid_counts,
214  int* grids,
215  long* index,
216  double* scales,
217  PyObject** sources) {
218  Grid_node* g;
219  int i = 0, j, k, n, grid_id;
220  int offset = 0;
221 
222  for (g = Parallel_grids[0]; g != NULL; g = g->next) {
223  if (g->node_flux_count > 0) {
224  g->node_flux_count = 0;
225  free(g->node_flux_scale);
226  free(g->node_flux_idx);
227  free(g->node_flux_src);
228  }
229  }
230  if (grid_count == 0)
231  return;
232  for (grid_id = 0, g = Parallel_grids[0]; g != NULL; grid_id++, g = g->next) {
233 #if NRNMPI
234  if (nrnmpi_use && dynamic_cast<ECS_Grid_node*>(g)) {
235  if (grid_id == grids[i])
236  n = grid_counts[i++];
237  else
238  n = 0;
239 
241  nrnmpi_int_allgather_inplace(g->proc_num_fluxes, 1);
242 
243  g->proc_flux_offsets[0] = 0;
244  for (j = 1; j < nrnmpi_numprocs; j++)
245  g->proc_flux_offsets[j] = g->proc_flux_offsets[j - 1] + g->proc_num_fluxes[j - 1];
246  g->node_flux_count = g->proc_flux_offsets[j - 1] + g->proc_num_fluxes[j - 1];
247  /*Copy array of the indexes and scales -- sources are evaluated at runtime*/
248  if (n > 0) {
249  g->node_flux_idx = (long*) malloc(g->node_flux_count * sizeof(long));
250  g->node_flux_scale = (double*) malloc(g->node_flux_count * sizeof(double));
251  g->node_flux_src = (PyObject**) allocopy(&sources[offset], n * sizeof(PyObject*));
252  }
253 
254  for (j = 0, k = g->proc_flux_offsets[nrnmpi_myid]; j < n; j++, k++) {
255  g->node_flux_idx[k] = index[offset + j];
256  g->node_flux_scale[k] = scales[offset + j];
257  }
258  nrnmpi_long_allgatherv_inplace(g->node_flux_idx,
259  g->proc_num_fluxes,
260  g->proc_flux_offsets);
261  nrnmpi_dbl_allgatherv_inplace(g->node_flux_scale,
262  g->proc_num_fluxes,
263  g->proc_flux_offsets);
264 
265  offset += n;
266  } else {
267  if (grid_id == grids[i]) {
268  g->node_flux_count = grid_counts[i];
269  if (grid_counts[i] > 0) {
270  g->node_flux_idx = (long*) allocopy(&index[offset],
271  grid_counts[i] * sizeof(long));
272  g->node_flux_scale = (double*) allocopy(&scales[offset],
273  grid_counts[i] * sizeof(double));
274  g->node_flux_src = (PyObject**) allocopy(&sources[offset],
275  grid_counts[i] * sizeof(PyObject*));
276  }
277  offset += grid_counts[i++];
278  }
279  }
280 #else
281  if (grid_id == grids[i]) {
282  g->node_flux_count = grid_counts[i];
283  if (grid_counts[i] > 0) {
284  g->node_flux_idx = (long*) allocopy(&index[offset], grid_counts[i] * sizeof(long));
285  g->node_flux_scale = (double*) allocopy(&scales[offset],
286  grid_counts[i] * sizeof(double));
287  g->node_flux_src = (PyObject**) allocopy(&sources[offset],
288  grid_counts[i] * sizeof(PyObject*));
289  }
290  offset += grid_counts[i++];
291  }
292 #endif
293  }
294 }
295 
297  long* index,
298  double* scales,
299  PyObject** sources) {
300  if (_node_flux_count != 0) {
301  free(_node_flux_idx);
302  free(_node_flux_scale);
303  free(_node_flux_src);
304  }
306  if (n > 0) {
307  _node_flux_idx = (long*) allocopy(index, n * sizeof(long));
308  _node_flux_scale = (double*) allocopy(scales, n * sizeof(double));
309  _node_flux_src = (PyObject**) allocopy(sources, n * sizeof(PyObject*));
310  }
311 }
312 
313 
315  long* index,
316  double* scale,
317  PyObject** source,
318  double dt,
319  double* states) {
320  for (size_t i = 0; i < n; i++) {
321  size_t j = index == nullptr ? i : index[i];
322  if (PyFloat_Check(source[i])) {
323  states[j] += dt * PyFloat_AsDouble(source[i]) / scale[i];
324  } else if (PyCallable_Check(source[i])) {
325  /* It is a Python function or a PyHocObject*/
326  if (PyObject_TypeCheck(source[i], hocobject_type)) {
327  auto src = (PyHocObject*) source[i];
328  /*TODO: check it is a reference */
329  if (src->type_ == PyHoc::HocRefNum) {
330  states[j] += dt * (src->u.x_) / scale[i];
331  } else {
332  states[j] += dt * *(src->u.px_) / scale[i];
333  }
334  } else {
335  auto result = nb::steal(PyObject_CallObject(source[i], nullptr));
336  if (PyFloat_Check(result.ptr())) {
337  states[j] += dt * PyFloat_AsDouble(result.ptr()) / scale[i];
338  } else if (PyLong_Check(result.ptr())) {
339  states[j] += dt * (double) PyLong_AsLong(result.ptr()) / scale[i];
340  } else if (PyInt_Check(result.ptr())) {
341  states[j] += dt * (double) PyInt_AsLong(result.ptr()) / scale[i];
342  } else {
343  PyErr_SetString(PyExc_Exception,
344  "node._include_flux callback did not return a number.\n");
345  }
346  }
347  } else {
348  PyErr_SetString(PyExc_Exception, "node._include_flux unrecognised source term.\n");
349  }
350  }
351 }
352 
353 
354 static void apply_node_flux1D(double dt, double* states) {
356 }
357 
358 extern "C" NRN_EXPORT void rxd_set_euler_matrix(int nrow,
359  int nnonzero,
360  long* nonzero_i,
361  long* nonzero_j,
362  double* nonzero_values,
363  double* c_diagonal) {
364  long i, j, idx;
365  double val;
366  unsigned int k, ps;
367  unsigned int* parent_count;
368  /*free old data*/
369  if (_rxd_a != NULL) {
370  free(_rxd_a);
371  free(_rxd_b);
372  free(_rxd_c);
373  free(_rxd_d);
374  free(_rxd_p);
375  free(_rxd_euler_nonzero_i);
376  free(_rxd_euler_nonzero_j);
378  _rxd_a = NULL;
379  }
380  diffusion = TRUE;
381 
382  _rxd_euler_nrow = nrow;
383  _rxd_euler_nnonzero = nnonzero;
384  _rxd_euler_nonzero_i = (long*) allocopy(nonzero_i, sizeof(long) * nnonzero);
385  _rxd_euler_nonzero_j = (long*) allocopy(nonzero_j, sizeof(long) * nnonzero);
386  _rxd_euler_nonzero_values = (double*) allocopy(nonzero_values, sizeof(double) * nnonzero);
387 
388  _rxd_a = (double*) calloc(nrow, sizeof(double));
389  _rxd_b = (double*) calloc(nrow, sizeof(double));
390  _rxd_c = (double*) calloc(nrow, sizeof(double));
391  _rxd_d = (double*) calloc(nrow, sizeof(double));
392  _rxd_p = (long*) malloc(nrow * sizeof(long));
393  parent_count = (unsigned int*) calloc(nrow, sizeof(unsigned int));
394 
395  for (idx = 0; idx < nrow; idx++)
396  _rxd_p[idx] = -1;
397 
398  for (idx = 0; idx < nnonzero; idx++) {
399  i = nonzero_i[idx];
400  j = nonzero_j[idx];
401  val = nonzero_values[idx];
402  if (i < j) {
403  _rxd_p[j] = i;
404  parent_count[i]++;
405  _rxd_a[j] = val;
406  } else if (i == j) {
407  _rxd_d[i] = val;
408  } else {
409  _rxd_b[i] = val;
410  }
411  }
412 
413  for (idx = 0; idx < nrow; idx++) {
414  _rxd_c[idx] = _rxd_d[idx] > 0 ? c_diagonal[idx] : 1.0;
415  }
416 
417  if (_rxd_num_zvi > 0) {
418  _rxd_zvi_child_count = (unsigned int*) malloc(_rxd_num_zvi * sizeof(unsigned int));
419  _rxd_zvi_child = (long**) malloc(_rxd_num_zvi * sizeof(long*));
420 
421  /* find children of zero-volume-indices */
422  for (i = 0; i < _rxd_num_zvi; i++) {
423  ps = parent_count[_rxd_zero_volume_indices[i]];
424  if (ps == 0) {
425  _rxd_zvi_child_count[i] = 0;
426  _rxd_zvi_child[i] = NULL;
427 
428  continue;
429  }
430  _rxd_zvi_child[i] = (long*) malloc(ps * sizeof(long));
431  _rxd_zvi_child_count[i] = ps;
432  for (j = 0, k = 0; k < ps; j++) {
433  if (_rxd_zero_volume_indices[i] == _rxd_p[j]) {
434  _rxd_zvi_child[i][k] = j;
435  k++;
436  }
437  }
438  }
439  }
440  free(parent_count);
441 }
442 
443 static void add_currents(double* result) {
444  long i, j, k, idx;
445  short side;
446  /*Add currents to the result*/
447  for (k = 0; k < _curr_count; k++)
449 
450  /*Subtract rxd induced currents*/
451  if (_membrane_flux) {
452  for (i = 0, k = 0; i < _memb_count; i++) {
453  for (j = 0; j < _memb_species_count[i]; j++, k++) {
454  for (side = 0; side < 2; side++) {
455  idx = _memb_cur_mapped[i][j][side];
456  if (idx != SPECIES_ABSENT) {
458  }
459  }
460  }
461  }
462  }
463 }
464 static void mul(int nnonzero,
465  long* nonzero_i,
466  long* nonzero_j,
467  const double* nonzero_values,
468  const double* v,
469  double* result) {
470  long i, j, k;
471  /* now loop through all the nonzero locations */
472  /* NOTE: this would be more efficient if not repeatedly doing the result[i] lookup */
473  for (k = 0; k < nnonzero; k++) {
474  i = *nonzero_i++;
475  j = *nonzero_j++;
476  result[i] -= (*nonzero_values++) * v[j];
477  }
478 }
479 
480 extern "C" NRN_EXPORT void set_setup(fptr* setup_fn) {
481  _setup = setup_fn;
482 }
483 
484 extern "C" NRN_EXPORT void set_initialize(fptr* initialize_fn) {
485  _initialize = initialize_fn;
487 }
488 
489 extern "C" NRN_EXPORT void set_setup_matrices(fptr* setup_matrices) {
490  _setup_matrices = setup_matrices;
491 }
492 
493 extern "C" NRN_EXPORT void set_setup_units(fptr* setup_units) {
494  _setup_units = setup_units;
495 }
496 
497 /* nrn_tree_solve modified from nrnoc/ldifus.c */
498 static void nrn_tree_solve(double* a,
499  double* b,
500  double* c,
501  double* dbase,
502  double* rhs,
503  long* pindex,
504  long n,
505  double dt) {
506  /*
507  treesolver
508 
509  a - above the diagonal
510  b - below the diagonal
511  c - used to define diagonal: diag = c + dt * dbase
512  dbase - together with c, used to define diagonal
513  rhs - right hand side, which is changed to the result
514  pindex - parent indices
515  n - number of states
516  */
517  long i, pin;
518  double* d = (double*) malloc(sizeof(double) * n);
519  double* myd;
520  double* myc;
521  double* mydbase;
522 
523  /* TODO: check that d non-null */
524 
525  /* construct diagonal */
526  myd = d;
527  mydbase = dbase;
528  myc = c;
529  for (i = 0; i < n; i++) {
530  *myd++ = *myc++ + dt * (*mydbase++);
531  }
532 
533  /* triang */
534  for (i = n - 1; i > 0; --i) {
535  pin = pindex[i];
536  if (pin > -1) {
537  double p;
538  p = dt * a[i] / d[i];
539  d[pin] -= dt * p * b[i];
540  rhs[pin] -= p * rhs[i];
541  }
542  }
543  /* bksub */
544  for (i = 0; i < n; ++i) {
545  pin = pindex[i];
546  if (pin > -1) {
547  rhs[i] -= dt * b[i] * rhs[pin];
548  }
549  rhs[i] /= d[i];
550  }
551 
552  free(d);
553 }
554 
555 
556 static void ode_solve(double dt, double* p1, double* p2) {
557  long i, j;
558  double* b = p1 + _cvode_offset;
559  double* y = p2 + _cvode_offset;
560  double *full_b, *full_y;
561  long* zvi = _rxd_zero_volume_indices;
562 
563  if (_rxd_num_zvi > 0) {
564  full_b = (double*) calloc(sizeof(double), num_states);
565  full_y = (double*) calloc(sizeof(double), num_states);
566  for (i = 0, j = 0; i < num_states; i++) {
567  if (i == zvi[j]) {
568  j++;
569  } else {
570  full_b[i] = b[i - j];
571  full_y[i] = y[i - j];
572  }
573  }
574  } else {
575  full_b = b;
576  full_y = y;
577  }
578  if (diffusion)
580 
581  do_ics_reactions(full_y, full_b, y, b);
582 
583  if (_rxd_num_zvi > 0) {
584  for (i = 0, j = 0; i < num_states; i++) {
585  if (i == zvi[j])
586  j++;
587  else
588  b[i - j] = full_b[i];
589  }
590  free(full_b);
591  free(full_y);
592  }
593 }
594 
595 static void ode_abs_tol(double* p1) {
596  int i, j;
597  double* y = p1 + _cvode_offset;
598  if (species_indices != NULL) {
599  SpeciesIndexList* list;
600  for (list = species_indices; list->next != NULL; list = list->next) {
601  for (i = 0, j = 0; i < list->length; i++) {
602  for (; j < _rxd_num_zvi && _rxd_zero_volume_indices[j] <= list->indices[i]; j++)
603  ;
604  y[list->indices[i] - j] *= list->atolscale;
605  }
606  }
607  }
608 }
609 
610 static void free_currents() {
611  int i, j;
612  if (!_membrane_flux)
613  return;
614  for (i = 0; i < _memb_count; i++) {
615  for (j = 0; j < _memb_species_count[i]; j++) {
616  free(_memb_cur_mapped[i][j]);
617  free(_memb_cur_mapped_ecs[i][j]);
618  }
619  free(_memb_cur_mapped[i]);
620  free(_memb_cur_mapped_ecs[i]);
621  }
622  _memb_cur_ptrs.clear();
623  free(_memb_cur_mapped);
624  free(_memb_species_count);
625  free(_cur_node_indices);
626  free(_rxd_induced_currents);
627  free(_rxd_flux_scale);
628  free(_membrane_lookup);
629  free(_memb_cur_mapped_ecs);
633 }
634 
635 extern "C" NRN_EXPORT void setup_currents(int num_currents,
636  int num_fluxes,
637  int* num_species,
638  int* node_idxs,
639  double* scales,
640  PyHocObject** ptrs,
641  int* mapped,
642  int* mapped_ecs) {
643  int i, j, k, id, side, count;
644  int* induced_currents_ecs_idx;
645  int* induced_currents_grid_id;
646  int* ecs_indices;
647  double* current_scales;
648  PyHocObject** ecs_ptrs;
649 
650  Grid_node* g;
651  ECS_Grid_node* grid;
652 
653 
654  free_currents();
655 
656  _memb_count = num_currents;
657  _memb_curr_total = num_fluxes;
658  _memb_species_count = (int*) malloc(sizeof(int) * num_currents);
659  memcpy(_memb_species_count, num_species, sizeof(int) * num_currents);
660 
661  _rxd_flux_scale = (double*) calloc(sizeof(double), num_fluxes);
662  // memcpy(_rxd_flux_scale,scales,sizeof(double)*num_currents);
663 
664  /*TODO: if memory is an issue - replace with a map*/
665  _membrane_lookup = (int*) malloc(sizeof(int) * num_states);
666  memset(_membrane_lookup, SPECIES_ABSENT, sizeof(int) * num_states);
667 
668  _memb_cur_ptrs.resize(num_currents);
669  _memb_cur_mapped_ecs = (int***) malloc(sizeof(int*) * num_currents);
670  _memb_cur_mapped = (int***) malloc(sizeof(int**) * num_currents);
671  induced_currents_ecs_idx = (int*) malloc(sizeof(int) * _memb_curr_total);
672  induced_currents_grid_id = (int*) malloc(sizeof(int) * _memb_curr_total);
673  // initialize memory here to allow currents from an intracellular species
674  // with no corresponding nrn_region='o' or Extracellular species
675  memset(induced_currents_ecs_idx, SPECIES_ABSENT, sizeof(int) * _memb_curr_total);
676 
677  for (i = 0, k = 0; i < num_currents; i++) {
678  _memb_cur_ptrs[i].resize(num_species[i]);
679  _memb_cur_mapped_ecs[i] = (int**) malloc(sizeof(int*) * num_species[i]);
680  _memb_cur_mapped[i] = (int**) malloc(sizeof(int*) * num_species[i]);
681 
682 
683  for (j = 0; j < num_species[i]; j++, k++) {
684  _memb_cur_ptrs[i][j] = ptrs[k]->u.px_;
685  _memb_cur_mapped[i][j] = (int*) malloc(2 * sizeof(int));
686  _memb_cur_mapped_ecs[i][j] = (int*) malloc(2 * sizeof(int));
687 
688 
689  for (side = 0; side < 2; side++) {
690  _memb_cur_mapped[i][j][side] = mapped[2 * k + side];
691  _memb_cur_mapped_ecs[i][j][side] = mapped_ecs[2 * k + side];
692  }
693  for (side = 0; side < 2; side++) {
694  if (_memb_cur_mapped[i][j][side] != SPECIES_ABSENT) {
696  _rxd_flux_scale[k] = scales[i];
697  if (_memb_cur_mapped[i][j][(side + 1) % 2] == SPECIES_ABSENT) {
698  induced_currents_grid_id[k] = _memb_cur_mapped_ecs[i][j][0];
699  induced_currents_ecs_idx[k] = _memb_cur_mapped_ecs[i][j][1];
700  }
701  }
702  }
703  }
704  }
706  _rxd_induced_currents_scale = (double*) calloc(_memb_curr_total, sizeof(double));
707  for (id = 0, g = Parallel_grids[0]; g != NULL; g = g->next, id++) {
708  grid = dynamic_cast<ECS_Grid_node*>(g);
709  if (grid == NULL)
710  continue; // ignore ICS grids
711 
712  for (count = 0, k = 0; k < _memb_curr_total; k++) {
713  if (induced_currents_grid_id[k] == id) {
715  count++;
716  }
717  }
718  if (count > 0) {
719  ecs_indices = (int*) malloc(count * sizeof(int));
720  ecs_ptrs = (PyHocObject**) malloc(count * sizeof(PyHocObject*));
721  for (i = 0, k = 0; k < _memb_curr_total; k++) {
722  if (induced_currents_grid_id[k] == id) {
723  ecs_indices[i] = induced_currents_ecs_idx[k];
724  ecs_ptrs[i++] = ptrs[k];
725  }
726  }
727  current_scales = grid->set_rxd_currents(count, ecs_indices, ecs_ptrs);
728  free(ecs_ptrs);
729 
730  for (i = 0, k = 0; k < _memb_curr_total; k++) {
731  if (induced_currents_grid_id[k] == id)
732  _rxd_induced_currents_scale[k] = current_scales[i];
733  }
734  }
735  }
736  /*index into arrays of nodes/states*/
737  _cur_node_indices = (int*) malloc(sizeof(int) * num_currents);
738  memcpy(_cur_node_indices, node_idxs, sizeof(int) * num_currents);
740  _rxd_induced_currents = (double*) malloc(sizeof(double) * _memb_curr_total);
741  free(induced_currents_ecs_idx);
742  free(induced_currents_grid_id);
743 }
744 
745 static void _currents(double* rhs) {
746  int i, j, k, idx, side;
747  Grid_node* g;
748  ECS_Grid_node* grid;
749  if (!_membrane_flux)
750  return;
752  for (g = Parallel_grids[0]; g != NULL; g = g->next) {
753  grid = dynamic_cast<ECS_Grid_node*>(g);
754  if (grid)
755  grid->induced_idx = 0;
756  }
757  for (i = 0, k = 0; i < _memb_count; i++) {
758  idx = _cur_node_indices[i];
759 
760  for (j = 0; j < _memb_species_count[i]; j++, k++) {
761  rhs[idx] -= _rxd_induced_currents[k];
763 
764  for (side = 0; side < 2; side++) {
765  if (_memb_cur_mapped[i][j][side] == SPECIES_ABSENT) {
766  /*Extracellular region is within the ECS grid*/
768  if (grid != NULL && _memb_cur_mapped[i][j][(side + 1) % 2] != SPECIES_ABSENT)
769  grid->local_induced_currents[grid->induced_idx++] =
771  }
772  }
773  }
774  }
775 }
776 
777 extern "C" NRN_EXPORT int rxd_nonvint_block(int method, int size, double* p1, double* p2, int) {
778  if (initialized) {
780  /*TODO: Exclude irrelevant (non-rxd) structural changes*/
781  /*Needed for node.include_flux*/
782  _setup_matrices();
783  }
784  }
785  switch (method) {
786  case 0:
787  _setup();
788  break;
789  case 1:
790  _initialize();
791  // TODO: is there a better place to initialize multicompartment reactions
792  Grid_node* grid;
793  ECS_Grid_node* g;
794  for (grid = Parallel_grids[0]; grid != NULL; grid = grid->next) {
795  g = dynamic_cast<ECS_Grid_node*>(grid);
796  if (g)
798  }
799  break;
800  case 2:
801  /* compute outward current to be subtracted from rhs */
802  _currents(p1);
803  break;
804  case 3:
805  /* conductance to be added to d */
806  break;
807  case 4:
808  /* fixed step solve */
809  _fadvance();
811  break;
812  case 5:
813  /* ode_count */
814  _cvode_offset = size;
817  case 6:
818  /* ode_reinit(y) */
819  _ode_reinit(p1);
820  _ecs_ode_reinit(p1);
821  break;
822  case 7:
823  /* ode_fun(t, y, ydot); from t and y determine ydot */
824  _rhs_variable_step(p1, p2);
825  _rhs_variable_step_ecs(p1, p2);
826  break;
827  case 8:
828  ode_solve(*dt_ptr, p1, p2); /*solve mx=b replace b with x */
829  /* TODO: we can probably reuse the dgadi code here... for now, we do nothing, which
830  * implicitly approximates the Jacobian as the identity matrix */
831  // y= p1 = states and b = p2 = RHS for x direction
832  ics_ode_solve(*dt_ptr, p1, p2);
833  break;
834  case 9:
835  // ode_jacobian(*dt_ptr, p1, p2); /* optionally prepare jacobian for fast ode_solve */
836  break;
837  case 10:
838  ode_abs_tol(p1);
839  ecs_atolscale(p1);
840  /* ode_abs_tol(y_abs_tolerance); fill with cvode.atol() * scalefactor */
841  break;
842  default:
843  printf("Unknown rxd_nonvint_block call: %d\n", method);
844  break;
845  }
846  return 0;
847 }
848 
849 
850 /*****************************************************************************
851  *
852  * Begin intracellular code
853  *
854  *****************************************************************************/
855 
856 
857 extern "C" NRN_EXPORT void register_rate(int nspecies,
858  int nparam,
859  int nregions,
860  int nseg,
861  int* sidx,
862  int necs,
863  int necsparam,
864  int* ecs_ids,
865  int* ecsidx,
866  int nmult,
867  double* mult,
868  PyHocObject** vptrs,
869  ReactionRate* f) {
870  int i, j, k, idx, ecs_id, ecs_index, ecs_offset;
871  unsigned char counted;
872  Grid_node* g;
873  ECS_Grid_node* grid;
874  ICSReactions* react = (ICSReactions*) malloc(sizeof(ICSReactions));
875  react->reaction = f;
876  react->num_species = nspecies;
877  react->num_regions = nregions;
878  react->num_params = nparam;
879  react->num_segments = nseg;
880  react->num_ecs_species = necs;
881  react->num_ecs_params = necsparam;
882  react->num_mult = nmult;
883  react->icsN = 0;
884  react->ecsN = 0;
885  if (vptrs != NULL) {
886  react->vptrs = (double**) malloc(nseg * sizeof(double*));
887  for (i = 0; i < nseg; i++)
888  react->vptrs[i] = static_cast<double*>(vptrs[i]->u.px_);
889  } else {
890  react->vptrs = NULL;
891  }
892  react->state_idx = (int***) malloc(nseg * sizeof(int**));
893  for (i = 0, idx = 0; i < nseg; i++) {
894  react->state_idx[i] = (int**) malloc((nspecies + nparam) * sizeof(int*));
895  for (j = 0; j < nspecies + nparam; j++) {
896  react->state_idx[i][j] = (int*) malloc(nregions * sizeof(int));
897  for (k = 0; k < nregions; k++, idx++) {
898  if (sidx[idx] < 0) {
899  react->state_idx[i][j][k] = SPECIES_ABSENT;
900  } else {
901  react->state_idx[i][j][k] = sidx[idx];
902  if (i == 0 && j < nspecies)
903  react->icsN++;
904  }
905  }
906  }
907  }
908  if (nmult > 0) {
909  react->mc_multiplier = (double**) malloc(nmult * sizeof(double*));
910  for (i = 0; i < nmult; i++) {
911  react->mc_multiplier[i] = (double*) malloc(nseg * sizeof(double));
912  memcpy(react->mc_multiplier[i], (mult + i * nseg), nseg * sizeof(double));
913  }
914  }
915 
916  if (react->num_ecs_species + react->num_ecs_params > 0) {
917  react->ecs_grid = (ECS_Grid_node**) malloc(react->num_ecs_species * sizeof(Grid_node*));
918  react->ecs_state = (double***) malloc(nseg * sizeof(double**));
919  react->ecs_index = (int**) malloc(nseg * sizeof(int*));
920  react->ecs_offset_index = (int*) malloc(react->num_ecs_species * sizeof(int));
921  for (i = 0; i < nseg; i++) {
922  react->ecs_state[i] = (double**) malloc((necs + necsparam) * sizeof(double*));
923  react->ecs_index[i] = (int*) malloc((necs + necsparam) * sizeof(int));
924  }
925  for (j = 0; j < necs + necsparam; j++) {
926  ecs_offset = num_states - _rxd_num_zvi;
927 
928  for (ecs_id = 0, g = Parallel_grids[0]; g != NULL; g = g->next, ecs_id++) {
929  if (ecs_id == ecs_ids[j]) {
930  grid = dynamic_cast<ECS_Grid_node*>(g);
931  assert(grid != NULL);
932  if (j < necs) {
933  react->ecs_grid[j] = grid;
934  react->ecs_offset_index[j] =
935  grid->add_multicompartment_reaction(nseg, &ecsidx[j], necs + necsparam);
936  }
937 
938  for (i = 0, counted = FALSE; i < nseg; i++) {
939  // nseg x nregion x nspecies
940  ecs_index = ecsidx[i * (necs + necsparam) + j];
941 
942  if (ecs_index >= 0) {
943  react->ecs_state[i][j] = &(grid->states[ecs_index]);
944  react->ecs_index[i][j] = ecs_offset + ecs_index;
945  if (j < necs && !counted) {
946  react->ecsN++;
947  counted = TRUE;
948  }
949  } else {
950  react->ecs_state[i][j] = NULL;
951  }
952  }
953  ecs_offset += grid->size_x * grid->size_y * grid->size_z;
954  }
955  }
956  }
957  } else {
958  react->ecs_state = NULL;
959  }
960  if (react->num_mult > 0)
961  react->mc_mult = (double*) malloc(react->num_mult * sizeof(double));
962 
963  if (react->num_ecs_species > 0) {
964  react->ecs_states_for_reaction = (double*) malloc(react->num_ecs_species * sizeof(double));
965  react->ecs_states_for_reaction_dx = (double*) malloc(react->num_ecs_species *
966  sizeof(double));
967  react->ecs_result = (double*) malloc(react->num_ecs_species * sizeof(double));
968  react->ecs_result_dx = (double*) malloc(react->num_ecs_species * sizeof(double));
969  react->ecsindex = (int*) malloc(react->num_ecs_species * sizeof(int));
970  }
971  if (react->num_ecs_params > 0)
972  react->ecs_params_for_reaction = (double*) calloc(react->num_ecs_params, sizeof(double));
973 
974  if (_membrane_flux) {
975  react->flux = (double**) malloc(react->icsN * sizeof(double*));
976  for (i = 0; i < react->icsN; i++)
977  react->flux[i] = (double*) malloc(react->num_regions * sizeof(double));
978  } else {
979  react->flux = nullptr;
980  }
981  react->states_for_reaction = (double**) malloc(react->num_species * sizeof(double*));
982  react->states_for_reaction_dx = (double**) malloc(react->num_species * sizeof(double*));
983 
984  react->result_array = (double**) malloc(react->num_species * sizeof(double*));
985  react->result_array_dx = (double**) malloc(react->num_species * sizeof(double*));
986 
987  for (i = 0; i < react->num_species; i++) {
988  react->states_for_reaction[i] = (double*) malloc(react->num_regions * sizeof(double));
989  react->states_for_reaction_dx[i] = (double*) malloc(react->num_regions * sizeof(double));
990 
991  react->result_array[i] = (double*) malloc(react->num_regions * sizeof(double));
992  react->result_array_dx[i] = (double*) malloc(react->num_regions * sizeof(double));
993  }
994 
995  react->params_for_reaction = (double**) malloc(react->num_params * sizeof(double*));
996  for (i = 0; i < react->num_params; i++)
997  react->params_for_reaction[i] = (double*) malloc(react->num_regions * sizeof(double));
998 
999  if (_reactions == NULL) {
1000  _reactions = react;
1001  react->next = NULL;
1002 
1003  } else {
1004  react->next = _reactions;
1005  _reactions = react;
1006  }
1007 
1008  for (g = Parallel_grids[0]; g != NULL; g = g->next) {
1009  grid = dynamic_cast<ECS_Grid_node*>(g);
1010  if (grid)
1012  }
1013 }
1014 
1015 extern "C" NRN_EXPORT void clear_rates() {
1016  ICSReactions *react, *prev;
1017  int i, j;
1018  for (react = _reactions; react != NULL;) {
1019  if (react->vptrs != NULL)
1020  free(react->vptrs);
1021  for (i = 0; i < react->num_segments; i++) {
1022  for (j = 0; j < react->num_species + react->num_params; j++) {
1023  free(react->state_idx[i][j]);
1024  }
1025  free(react->state_idx[i]);
1026 
1027  if (react->num_ecs_species + react->num_ecs_params > 0) {
1028  free(react->ecs_state[i]);
1029  free(react->ecs_index[i]);
1030  }
1031  }
1032  free(react->state_idx);
1033  if (react->num_ecs_species + react->num_ecs_params > 0) {
1034  free(react->ecs_index);
1035  free(react->ecs_state);
1036  free(react->ecs_offset_index);
1037  free(react->ecs_grid);
1038  }
1039  if (react->num_mult > 0) {
1040  for (i = 0; i < react->num_mult; i++)
1041  free(react->mc_multiplier[i]);
1042  free(react->mc_multiplier);
1043  }
1044 
1045  prev = react;
1046 
1047  if (react->num_mult > 0)
1048  free(react->mc_mult);
1049  if (react->flux != NULL) {
1050  for (i = 0; i < react->icsN; i++)
1051  free(react->flux[i]);
1052  free(react->flux);
1053  }
1054  if (react->num_ecs_species > 0) {
1055  free(react->ecs_states_for_reaction);
1056  free(react->ecs_states_for_reaction_dx);
1057  free(react->ecs_result);
1058  free(react->ecs_result_dx);
1059  free(react->ecsindex);
1060  }
1061  for (i = 0; i < react->num_species; i++) {
1062  free(react->states_for_reaction[i]);
1063  free(react->states_for_reaction_dx[i]);
1064  free(react->result_array[i]);
1065  free(react->result_array_dx[i]);
1066  }
1067  free(react->states_for_reaction);
1068  free(react->states_for_reaction_dx);
1069 
1070  free(react->result_array);
1071  free(react->result_array_dx);
1072  for (i = 0; i < react->num_params; i++) {
1073  free(react->params_for_reaction[i]);
1074  }
1075  free(react->params_for_reaction);
1076  if (react->num_ecs_params > 0) {
1077  free(react->ecs_params_for_reaction);
1078  }
1079  react = react->next;
1080  free(prev);
1081  }
1082  _reactions = NULL;
1083  /*clear extracellular reactions*/
1084  clear_rates_ecs();
1085  // There are NUM_THREADS-1 std::thread objects alive, and these need to be
1086  // cleaned up before exit (otherwise the std::thread destructor will call
1087  // std::terminate.).
1088  set_num_threads(1);
1089 }
1090 
1091 
1092 extern "C" NRN_EXPORT void species_atolscale(int id, double scale, int len, int* idx) {
1093  SpeciesIndexList* list;
1095  if (species_indices != NULL) {
1096  for (list = species_indices, prev = NULL; list != NULL; list = list->next) {
1097  if (list->id == id) {
1098  list->atolscale = scale;
1099  return;
1100  }
1101  prev = list;
1102  }
1103  prev->next = (SpeciesIndexList*) malloc(sizeof(SpeciesIndexList));
1104  list = prev->next;
1105  } else {
1106  species_indices = (SpeciesIndexList*) malloc(sizeof(SpeciesIndexList));
1107  list = species_indices;
1108  }
1109  list->id = id;
1110  list->indices = (int*) malloc(sizeof(int) * len);
1111  memcpy(list->indices, idx, sizeof(int) * len);
1112  list->length = len;
1113  list->atolscale = scale;
1114  list->next = NULL;
1115 }
1116 
1117 extern "C" NRN_EXPORT void remove_species_atolscale(int id) {
1118  SpeciesIndexList* list;
1120  for (list = species_indices, prev = NULL; list != NULL; prev = list, list = list->next) {
1121  if (list->id == id) {
1122  if (prev == NULL)
1123  species_indices = list->next;
1124  else
1125  prev->next = list->next;
1126  free(list->indices);
1127  free(list);
1128  break;
1129  }
1130  }
1131 }
1132 
1133 extern "C" NRN_EXPORT void setup_solver(double* my_states,
1134  int my_num_states,
1135  long* zvi,
1136  int num_zvi) {
1137  free_currents();
1138  states = my_states;
1139  num_states = my_num_states;
1140  free_zvi_child();
1141  _rxd_num_zvi = num_zvi;
1144  if (num_zvi == 0)
1146  else
1147  _rxd_zero_volume_indices = (long*) allocopy(zvi, num_zvi * sizeof(long));
1148  dt_ptr = &nrn_threads->_dt;
1149  t_ptr = &nrn_threads->_t;
1151  initialized = TRUE;
1153 }
1154 
1155 void TaskQueue_add_task(TaskQueue* q, void* (*task)(void*), void* args, void* result) {
1156  auto* t = new TaskList{};
1157  t->task = task;
1158  t->args = args;
1159  t->result = result;
1160  t->next = nullptr;
1161 
1162  // Add task to the queue
1163  {
1164  std::lock_guard<std::mutex> _{q->task_mutex};
1165  if (!q->first) {
1166  // empty queue
1167  q->first = t;
1168  q->last = t;
1169  } else {
1170  // queue not empty
1171  q->last->next = t;
1172  q->last = t;
1173  }
1174  {
1175  std::lock_guard<std::mutex> _{q->waiting_mutex};
1176  ++q->length;
1177  }
1178  }
1179 
1180  // signal a waiting thread that there is a new task to pick up
1181  q->task_cond.notify_one();
1182 }
1183 
1184 void TaskQueue_exe_tasks(std::size_t thread_index, TaskQueue* q) {
1185  for (;;) {
1186  // Wait for a task to be available in the queue, then execute it.
1187  {
1188  TaskList* job{};
1189  {
1190  std::unique_lock<std::mutex> lock{q->task_mutex};
1191  // Wait until either for a new task to be received or for this
1192  // thread to be told to exit.
1193  q->task_cond.wait(lock,
1194  [q, thread_index] { return q->first || q->exit[thread_index]; });
1195  if (q->exit[thread_index]) {
1196  return;
1197  }
1198  job = q->first;
1199  q->first = job->next;
1200  }
1201  // Execute the task
1202  job->result = job->task(job->args);
1203  delete job;
1204  }
1205  // Decrement the list length, if it's now empty then broadcast that to
1206  // the master thread, which may be waiting for the queue to be empty.
1207  auto const new_length = [q] {
1208  std::lock_guard<std::mutex> _{q->waiting_mutex};
1209  return --(q->length);
1210  }();
1211  // The immediately-executed lambda means we release the mutex before
1212  // calling notify_one()
1213  if (new_length == 0) {
1214  // Queue is empty. Notify the main thread, which may be blocking on
1215  // this condition.
1216  q->waiting_cond.notify_one();
1217  }
1218  }
1219 }
1220 
1221 
1222 extern "C" NRN_EXPORT void set_num_threads(const int n) {
1223  assert(n > 0);
1224  assert(NUM_THREADS > 0);
1225  // n and NUM_THREADS include the main thread, old_num and new_num refer to
1226  // the number of std::thread workers
1227  std::size_t const old_num = NUM_THREADS - 1;
1228  std::size_t const new_num = n - 1;
1229  assert(old_num == Threads.size());
1230  assert(old_num == task_queue.exit.size());
1231  if (new_num < old_num) {
1232  // Kill some threads. First, wait until the queue is empty.
1234  // Now signal to the threads that are to be killed that they need to
1235  // exit.
1236  {
1237  std::lock_guard<std::mutex> _{task_queue.task_mutex};
1238  for (auto k = new_num; k < old_num; ++k) {
1239  task_queue.exit[k] = true;
1240  }
1241  }
1242  task_queue.task_cond.notify_all();
1243  // Finally, join those threads and destroy the std::thread objects.
1244  for (auto k = new_num; k < old_num; ++k) {
1245  Threads[k].join();
1246  }
1247  // And update the structures
1248  {
1249  std::lock_guard<std::mutex> _{task_queue.task_mutex};
1250  Threads.resize(new_num);
1251  task_queue.exit.resize(new_num);
1252  }
1253  } else if (new_num > old_num) {
1254  // Create some threads
1255  std::lock_guard<std::mutex> _{task_queue.task_mutex};
1256  task_queue.exit.reserve(new_num);
1257  Threads.reserve(new_num);
1258  for (auto k = old_num; k < new_num; ++k) {
1259  assert(k == Threads.size());
1260  Threads.emplace_back(TaskQueue_exe_tasks, k, &task_queue);
1261  task_queue.exit.emplace_back(false);
1262  }
1263  }
1264  assert(new_num == Threads.size());
1265  assert(new_num == task_queue.exit.size());
1267  NUM_THREADS = n;
1268 }
1269 
1271  // Wait till the queue is empty
1272  std::unique_lock<std::mutex> lock{q->waiting_mutex};
1273  q->waiting_cond.wait(lock, [q] { return q->length == 0; });
1274 }
1275 
1276 extern "C" NRN_EXPORT int get_num_threads(void) {
1277  return NUM_THREADS;
1278 }
1279 
1280 
1281 void _fadvance(void) {
1282  double dt = *dt_ptr;
1283  long i;
1284  /*variables for diffusion*/
1285  double* rhs;
1286  long* zvi = _rxd_zero_volume_indices;
1287 
1288  rhs = (double*) calloc(num_states, sizeof(double));
1289  /*diffusion*/
1290  if (diffusion)
1295  states,
1296  rhs);
1297 
1298  add_currents(rhs);
1299 
1300  /* multiply rhs vector by dt */
1301  for (i = 0; i < num_states; i++) {
1302  rhs[i] *= dt;
1303  }
1304 
1305  if (diffusion)
1307 
1308  /* increment states by rhs which is now really deltas */
1309  for (i = 0; i < num_states; i++) {
1310  states[i] += rhs[i];
1311  }
1312 
1313  /* clear zero volume indices (conservation nodes) */
1314  for (i = 0; i < _rxd_num_zvi; i++) {
1315  states[zvi[i]] = 0;
1316  }
1317 
1318  free(rhs);
1319 
1320  /*reactions*/
1322 
1323  /*node fluxes*/
1325 
1327 }
1328 
1329 
1330 void _ode_reinit(double* y) {
1331  long i, j;
1332  long* zvi = _rxd_zero_volume_indices;
1333  y += _cvode_offset;
1334  /*Copy states to CVode*/
1335  if (_rxd_num_zvi > 0) {
1336  for (i = 0, j = 0; i < num_states; i++) {
1337  if (zvi[j] == i)
1338  j++;
1339  else {
1340  y[i - j] = states[i];
1341  }
1342  }
1343  } else {
1344  memcpy(y, states, sizeof(double) * num_states);
1345  }
1346 }
1347 
1348 
1349 void _rhs_variable_step(const double* p1, double* p2) {
1350  Grid_node* grid;
1351  long i, j, p, c;
1352  unsigned int k;
1353  const unsigned char calculate_rhs = p2 == NULL ? 0 : 1;
1354  const double* my_states = p1 + _cvode_offset;
1355  double* ydot = p2 + _cvode_offset;
1356  /*variables for diffusion*/
1357  double* rhs;
1358  long* zvi = _rxd_zero_volume_indices;
1359 
1360  double const* const orig_states3d = p1 + states_cvode_offset;
1361  double* const orig_ydot3d = p2 + states_cvode_offset;
1362 
1363  /*Copy states from CVode*/
1364  if (_rxd_num_zvi > 0) {
1365  for (i = 0, j = 0; i < num_states; i++) {
1366  if (zvi[j] == i)
1367  j++;
1368  else {
1369  states[i] = my_states[i - j];
1370  }
1371  }
1372  } else {
1373  memcpy(states, my_states, sizeof(double) * num_states);
1374  }
1375 
1376  if (diffusion) {
1377  for (i = 0; i < _rxd_num_zvi; i++) {
1378  j = zvi[i];
1379  p = _rxd_p[j];
1380  states[j] = (p > 0 ? -(_rxd_b[j] / _rxd_d[j]) * states[p] : 0);
1381  for (k = 0; k < _rxd_zvi_child_count[i]; k++) {
1382  c = _rxd_zvi_child[i][k];
1383  states[j] -= (_rxd_a[c] / _rxd_d[j]) * states[c];
1384  }
1385  }
1386  }
1387 
1389 
1390  if (!calculate_rhs) {
1391  for (i = 0; i < _rxd_num_zvi; i++)
1392  states[zvi[i]] = 0;
1393  return;
1394  }
1395 
1396  /*diffusion*/
1397  rhs = (double*) calloc(num_states, sizeof(double));
1398 
1399  if (diffusion)
1404  states,
1405  rhs);
1406 
1407  /*reactions*/
1408  memset(&ydot[num_states - _rxd_num_zvi], 0, sizeof(double) * _ecs_count);
1410 
1411 
1412  const double* states3d = orig_states3d;
1413  double* ydot3d = orig_ydot3d;
1414  int grid_size;
1415  for (grid = Parallel_grids[0]; grid != NULL; grid = grid->next) {
1416  grid_size = grid->size_x * grid->size_y * grid->size_z;
1417  if (grid->hybrid) {
1418  grid->variable_step_hybrid_connections(states3d, ydot3d, states, rhs);
1419  }
1420  ydot3d += grid_size;
1421  states3d += grid_size;
1422  }
1423 
1424  /*Add currents to the result*/
1425  add_currents(rhs);
1426 
1427  /*Add node fluxes to the result*/
1428  apply_node_flux1D(1.0, rhs);
1429 
1430  /* increment states by rhs which is now really deltas */
1431  if (_rxd_num_zvi > 0) {
1432  for (i = 0, j = 0; i < num_states; i++) {
1433  if (zvi[j] == i) {
1434  states[i] = 0;
1435  j++;
1436  } else {
1437  ydot[i - j] = rhs[i];
1438  }
1439  }
1440  } else {
1441  memcpy(ydot, rhs, sizeof(double) * num_states);
1442  }
1443  free(rhs);
1444 }
1445 
1446 void get_reaction_rates(ICSReactions* react, double* states, double* rates, double* ydot) {
1447  int segment;
1448  int i, j, k, idx;
1449  double v = 0;
1450  for (i = 0; i < react->num_ecs_species; i++)
1451  react->ecsindex[i] = react->ecs_grid[i]->react_offsets[react->ecs_offset_index[i]];
1452 
1453  if (_membrane_flux) {
1454  for (i = 0; i < react->icsN; i++)
1455  memset(react->flux[i], 0, react->num_regions * sizeof(double));
1456  }
1457  for (segment = 0; segment < react->num_segments; segment++) {
1458  for (i = 0; i < react->num_species; i++) {
1459  for (j = 0; j < react->num_regions; j++) {
1460  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1461  react->states_for_reaction[i][j] = states[react->state_idx[segment][i][j]];
1462  } else {
1463  react->states_for_reaction[i][j] = NAN;
1464  }
1465  }
1466  memset(react->result_array[i], 0, react->num_regions * sizeof(double));
1467  }
1468  for (k = 0; i < react->num_species + react->num_params; i++, k++) {
1469  for (j = 0; j < react->num_regions; j++) {
1470  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1471  react->params_for_reaction[k][j] = states[react->state_idx[segment][i][j]];
1472  } else {
1473  react->params_for_reaction[k][j] = NAN;
1474  }
1475  }
1476  }
1477 
1478  for (i = 0; i < react->num_ecs_species; i++) {
1479  if (react->ecs_state[segment][i] != NULL) {
1480  react->ecs_states_for_reaction[i] = *(react->ecs_state[segment][i]);
1481  } else {
1482  react->ecs_states_for_reaction[i] = NAN;
1483  }
1484  }
1485  for (k = 0; i < react->num_ecs_species + react->num_ecs_params; i++, k++) {
1486  if (react->ecs_state[segment][i] != NULL) {
1487  react->ecs_params_for_reaction[k] = *(react->ecs_state[segment][i]);
1488  } else {
1489  react->ecs_params_for_reaction[k] = NAN;
1490  }
1491  }
1492  memset(react->ecs_result, 0, react->num_ecs_species * sizeof(double));
1493 
1494  for (i = 0; i < react->num_mult; i++) {
1495  react->mc_mult[i] = react->mc_multiplier[i][segment];
1496  }
1497 
1498  if (react->vptrs != NULL) {
1499  v = *(react->vptrs[segment]);
1500  }
1501 
1502  react->reaction(react->states_for_reaction,
1503  react->params_for_reaction,
1504  react->result_array,
1505  react->mc_mult,
1506  react->ecs_states_for_reaction,
1507  react->ecs_params_for_reaction,
1508  react->ecs_result,
1509  react->flux,
1510  v);
1511 
1512  for (i = 0; i < react->num_species; i++) {
1513  for (j = 0; j < react->num_regions; j++) {
1514  idx = react->state_idx[segment][i][j];
1515  if (idx != SPECIES_ABSENT) {
1518  _rxd_flux_scale[_membrane_lookup[idx]] * react->flux[i][j];
1519  }
1520  if (rates != NULL) {
1521  rates[idx] += react->result_array[i][j];
1522  }
1523  }
1524  }
1525  }
1526  if (ydot != NULL) {
1527  for (i = 0; i < react->num_ecs_species; i++) {
1528  if (react->ecs_state[segment][i] != NULL)
1529  react->ecs_grid[i]->all_reaction_states[react->ecsindex[i]++] =
1530  react->ecs_result[i];
1531  // ydot[react->ecs_index[segment][i]] += ecs_result[i];
1532  }
1533  }
1534  }
1535 }
1536 
1538  double* states,
1539  double* bval,
1540  double* cvode_states,
1541  double* cvode_b) {
1542  int segment;
1543  int i, j, k, idx, jac_i, jac_j, jac_idx;
1544  int N = react->icsN + react->ecsN; /*size of Jacobian (number species*regions for a segments)*/
1545  double pd;
1546  double dt = *dt_ptr;
1547  double dx = FLT_EPSILON;
1548  OcFullMatrix jacobian(N, N);
1549  auto b = std::make_unique<IvocVect>(N);
1550  auto x = std::make_unique<IvocVect>(N);
1551  double v = 0;
1552 
1553  if (react->num_ecs_species > 0) {
1554  for (i = 0; i < react->num_ecs_species; i++)
1555  react->ecsindex[i] = react->ecs_grid[i]->react_offsets[react->ecs_offset_index[i]];
1556  }
1557 
1558  for (segment = 0; segment < react->num_segments; segment++) {
1559  if (react->vptrs != NULL)
1560  v = *(react->vptrs[segment]);
1561 
1562  for (i = 0; i < react->num_species; i++) {
1563  for (j = 0; j < react->num_regions; j++) {
1564  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1565  react->states_for_reaction[i][j] = states[react->state_idx[segment][i][j]];
1566  react->states_for_reaction_dx[i][j] = react->states_for_reaction[i][j];
1567  } else {
1569  react->states_for_reaction_dx[i][j] = react->states_for_reaction[i][j];
1570  }
1571  }
1572  memset(react->result_array[i], 0, react->num_regions * sizeof(double));
1573  memset(react->result_array_dx[i], 0, react->num_regions * sizeof(double));
1574  }
1575  for (k = 0; i < react->num_species + react->num_params; i++, k++) {
1576  for (j = 0; j < react->num_regions; j++) {
1577  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1578  react->params_for_reaction[k][j] = states[react->state_idx[segment][i][j]];
1579  } else {
1581  }
1582  }
1583  }
1584 
1585 
1586  for (i = 0; i < react->num_ecs_species; i++) {
1587  if (react->ecs_state[segment][i] != NULL) {
1588  if (cvode_states != NULL) {
1589  react->ecs_states_for_reaction[i] = cvode_states[react->ecs_index[segment][i]];
1590  } else {
1591  react->ecs_states_for_reaction[i] = *(react->ecs_state[segment][i]);
1592  }
1594  }
1595  }
1596  for (k = 0; i < react->num_ecs_species + react->num_ecs_params; i++, k++) {
1597  if (react->ecs_state[segment][i] != NULL) {
1598  react->ecs_params_for_reaction[k] = *(react->ecs_state[segment][i]);
1599  }
1600  }
1601 
1602  if (react->num_ecs_species > 0) {
1603  memset(react->ecs_result, 0, react->num_ecs_species * sizeof(double));
1604  memset(react->ecs_result_dx, 0, react->num_ecs_species * sizeof(double));
1605  }
1606 
1607  for (i = 0; i < react->num_mult; i++) {
1608  react->mc_mult[i] = react->mc_multiplier[i][segment];
1609  }
1610 
1611  react->reaction(react->states_for_reaction,
1612  react->params_for_reaction,
1613  react->result_array,
1614  react->mc_mult,
1615  react->ecs_states_for_reaction,
1616  react->ecs_params_for_reaction,
1617  react->ecs_result,
1618  NULL,
1619  v);
1620 
1621  /*Calculate I - Jacobian for ICS reactions*/
1622  for (i = 0, idx = 0; i < react->num_species; i++) {
1623  for (j = 0; j < react->num_regions; j++) {
1624  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1625  if (bval == NULL)
1626  b->elem(idx) = dt * react->result_array[i][j];
1627  else
1628  b->elem(idx) = bval[react->state_idx[segment][i][j]];
1629 
1630 
1631  // set up the changed states array
1632  react->states_for_reaction_dx[i][j] += dx;
1633 
1634  /* TODO: Handle approximating the Jacobian at a function upper
1635  * limit, e.g. acos(1)
1636  */
1637  react->reaction(react->states_for_reaction_dx,
1638  react->params_for_reaction,
1639  react->result_array_dx,
1640  react->mc_mult,
1641  react->ecs_states_for_reaction,
1642  react->ecs_params_for_reaction,
1643  react->ecs_result_dx,
1644  NULL,
1645  v);
1646 
1647  for (jac_i = 0, jac_idx = 0; jac_i < react->num_species; jac_i++) {
1648  for (jac_j = 0; jac_j < react->num_regions; jac_j++) {
1649  // pd is our Jacobian approximated
1650  if (react->state_idx[segment][jac_i][jac_j] != SPECIES_ABSENT) {
1651  pd = (react->result_array_dx[jac_i][jac_j] -
1652  react->result_array[jac_i][jac_j]) /
1653  dx;
1654  jacobian(jac_idx, idx) = (idx == jac_idx) - dt * pd;
1655  jac_idx += 1;
1656  }
1657  react->result_array_dx[jac_i][jac_j] = 0;
1658  }
1659  }
1660  for (jac_i = 0; jac_i < react->num_ecs_species; jac_i++) {
1661  // pd is our Jacobian approximated
1662  if (react->ecs_state[segment][jac_i] != NULL) {
1663  pd = (react->ecs_result_dx[jac_i] - react->ecs_result[jac_i]) / dx;
1664  jacobian(jac_idx, idx) = -dt * pd;
1665  jac_idx += 1;
1666  }
1667  react->ecs_result_dx[jac_i] = 0;
1668  }
1669  // reset dx array
1670  react->states_for_reaction_dx[i][j] -= dx;
1671  idx++;
1672  }
1673  }
1674  }
1675 
1676  /*Calculate I - Jacobian for MultiCompartment ECS reactions*/
1677  for (i = 0; i < react->num_ecs_species; i++) {
1678  if (react->ecs_state[segment][i] != NULL) {
1679  if (bval == NULL)
1680  b->elem(idx) = dt * react->ecs_result[i];
1681  else
1682  b->elem(idx) = cvode_b[react->ecs_index[segment][i]];
1683 
1684 
1685  // set up the changed states array
1686  react->ecs_states_for_reaction_dx[i] += dx;
1687 
1688  /* TODO: Handle approximating the Jacobian at a function upper
1689  * limit, e.g. acos(1)
1690  */
1691  react->reaction(react->states_for_reaction,
1692  react->params_for_reaction,
1693  react->result_array_dx,
1694  react->mc_mult,
1696  react->ecs_params_for_reaction,
1697  react->ecs_result_dx,
1698  NULL,
1699  v);
1700 
1701  for (jac_i = 0, jac_idx = 0; jac_i < react->num_species; jac_i++) {
1702  for (jac_j = 0; jac_j < react->num_regions; jac_j++) {
1703  // pd is our Jacobian approximated
1704  if (react->state_idx[segment][jac_i][jac_j] != SPECIES_ABSENT) {
1705  pd = (react->result_array_dx[jac_i][jac_j] -
1706  react->result_array[jac_i][jac_j]) /
1707  dx;
1708  jacobian(jac_idx, idx) = -dt * pd;
1709  jac_idx += 1;
1710  }
1711  }
1712  }
1713  for (jac_i = 0; jac_i < react->num_ecs_species; jac_i++) {
1714  // pd is our Jacobian approximated
1715  if (react->ecs_state[segment][jac_i] != NULL) {
1716  pd = (react->ecs_result_dx[jac_i] - react->ecs_result[jac_i]) / dx;
1717  jacobian(jac_idx, idx) = (idx == jac_idx) - dt * pd;
1718  jac_idx += 1;
1719  } else {
1720  jacobian(idx, idx) = 1.0;
1721  }
1722  // reset dx array
1723  react->ecs_states_for_reaction_dx[i] -= dx;
1724  }
1725  idx++;
1726  }
1727  }
1728  // solve for x, destructively
1729  jacobian.solv(b.get(), x.get(), false);
1730 
1731  if (bval != NULL) // variable-step
1732  {
1733  for (i = 0, jac_idx = 0; i < react->num_species; i++) {
1734  for (j = 0; j < react->num_regions; j++) {
1735  idx = react->state_idx[segment][i][j];
1736  if (idx != SPECIES_ABSENT) {
1737  bval[idx] = x->elem(jac_idx++);
1738  }
1739  }
1740  }
1741  for (i = 0; i < react->num_ecs_species; i++) {
1742  if (react->ecs_state[segment][i] != NULL)
1743  react->ecs_grid[i]->all_reaction_states[react->ecsindex[i]++] = x->elem(
1744  jac_idx++);
1745  // cvode_b[react->ecs_index[segment][i]] = x->elem(jac_idx++);
1746  }
1747  } else // fixed-step
1748  {
1749  for (i = 0, jac_idx = 0; i < react->num_species; i++) {
1750  for (j = 0; j < react->num_regions; j++) {
1751  idx = react->state_idx[segment][i][j];
1752  if (idx != SPECIES_ABSENT)
1753  states[idx] += x->elem(jac_idx++);
1754  }
1755  }
1756  for (i = 0; i < react->num_ecs_species; i++) {
1757  if (react->ecs_state[segment][i] != NULL)
1758  react->ecs_grid[i]->all_reaction_states[react->ecsindex[i]++] = x->elem(
1759  jac_idx++);
1760  }
1761  }
1762  }
1763 }
1764 
1765 void do_ics_reactions(double* states, double* b, double* cvode_states, double* cvode_b) {
1766  ICSReactions* react;
1767  for (react = _reactions; react != NULL; react = react->next) {
1768  if (react->icsN + react->ecsN > 0)
1769  solve_reaction(react, states, b, cvode_states, cvode_b);
1770  }
1771 }
1772 
1773 void get_all_reaction_rates(double* states, double* rates, double* ydot) {
1774  ICSReactions* react;
1775  if (_membrane_flux)
1776  memset(_rxd_induced_currents, 0, sizeof(double) * _memb_curr_total);
1777  for (react = _reactions; react != NULL; react = react->next) {
1778  if (react->icsN + react->ecsN > 0)
1779  get_reaction_rates(react, states, rates, ydot);
1780  }
1781 }
1782 
1783 /*****************************************************************************
1784  *
1785  * End intracellular code
1786  *
1787  *****************************************************************************/
int induced_idx
Definition: grids.h:187
double * set_rxd_currents(int, int *, PyHocObject **)
Definition: grids.cpp:935
void initialize_multicompartment_reaction()
Definition: grids.cpp:1119
int add_multicompartment_reaction(int, int *, int)
Definition: grids.cpp:1068
int * react_offsets
Definition: grids.h:188
double * all_reaction_states
Definition: grids.h:200
double * local_induced_currents
Definition: grids.h:202
int node_flux_count
Definition: grids.h:138
bool hybrid
Definition: grids.h:101
int * proc_num_fluxes
Definition: grids.h:113
int size_y
Definition: grids.h:92
virtual void variable_step_hybrid_connections(const double *cvode_states_3d, double *const ydot_3d, const double *cvode_states_1d, double *const ydot_1d)=0
long * node_flux_idx
Definition: grids.h:139
int size_x
Definition: grids.h:91
double * states
Definition: grids.h:86
int * proc_flux_offsets
Definition: grids.h:112
double * node_flux_scale
Definition: grids.h:140
Grid_node * next
Definition: grids.h:84
PyObject ** node_flux_src
Definition: grids.h:141
int size_z
Definition: grids.h:93
#define v
Definition: md1redef.h:11
#define id
Definition: md1redef.h:41
#define i
Definition: md1redef.h:19
static double jacobian(void *v)
Definition: cvodeobj.cpp:245
static RNG::key_type k
Definition: nrnran123.cpp:9
Grid_node * Parallel_grids[100]
Definition: grids.cpp:17
void(double **, double **, double **, double *, double *, double *, double *, double **, double) ReactionRate
Definition: grids.h:57
#define TRUE
Definition: grids.h:22
#define FALSE
Definition: grids.h:23
static int c
Definition: hoc.cpp:169
#define assert(ex)
Definition: hocassrt.h:24
static int ode_count(int type)
Definition: kschan.cpp:93
#define rhs
Definition: lineq.h:6
printf
Definition: extdef.h:5
Item * prev(Item *item)
Definition: list.cpp:94
static int nrnmpi_use
Definition: multisplit.cpp:41
@ HocRefNum
Definition: nrnpython.h:62
if(ncell==0)
Definition: cellorder.cpp:785
Definition: rxd.cpp:36
TaskQueue task_queue
Definition: rxd.cpp:38
std::vector< std::thread > Threads
Definition: rxd.cpp:37
Definition: bimap.hpp:13
#define NRN_EXPORT
Definition: nrn_export.hpp:6
int const size_t const size_t n
Definition: nrngsl.h:10
size_t q
size_t p
size_t j
short index
Definition: cabvars.h:11
_object PyObject
Definition: nrnpy.h:12
#define PyInt_AsLong
Definition: nrnpython.h:27
#define PyInt_Check
Definition: nrnpython.h:24
#define lock
int nrnmpi_myid
int _memb_curr_total
Definition: rxd.cpp:86
NRN_EXPORT void rxd_set_no_diffusion()
Definition: rxd.cpp:145
double * dt_ptr
Definition: grids.cpp:15
NRN_EXPORT void setup_solver(double *my_states, int my_num_states, long *zvi, int num_zvi)
Definition: rxd.cpp:1133
std::vector< neuron::container::data_handle< double > > _curr_ptrs
Definition: rxd.cpp:81
int ** _memb_cur_charges
Definition: rxd.cpp:102
int prev_structure_change_cnt
Definition: rxd.cpp:27
NRN_EXPORT void rxd_include_node_flux3D(int grid_count, int *grid_counts, int *grids, long *index, double *scales, PyObject **sources)
Definition: rxd.cpp:212
NRN_EXPORT int get_num_threads(void)
Definition: rxd.cpp:1276
int _node_flux_count
Definition: rxd.cpp:112
NRN_EXPORT void rxd_setup_curr_ptrs(int num_currents, int *curr_index, double *curr_scale, PyHocObject **curr_ptrs)
Definition: rxd.cpp:180
static void free_zvi_child()
Definition: rxd.cpp:117
int _rxd_num_zvi
Definition: rxd.cpp:53
void TaskQueue_exe_tasks(std::size_t thread_index, TaskQueue *q)
Definition: rxd.cpp:1184
void get_all_reaction_rates(double *states, double *rates, double *ydot)
Definition: rxd.cpp:1773
NRN_EXPORT void set_num_threads(const int n)
Definition: rxd.cpp:1222
int * _cur_node_indices
Definition: rxd.cpp:93
int _num_reactions
Definition: rxd.cpp:77
NRN_EXPORT void remove_species_atolscale(int id)
Definition: rxd.cpp:1117
NRN_EXPORT void set_initialize(fptr *initialize_fn)
Definition: rxd.cpp:484
int _rxd_euler_nrow
Definition: rxd.cpp:53
double * _rxd_d
Definition: rxd.cpp:61
double * _rxd_induced_currents_scale
Definition: rxd.cpp:92
NRN_EXPORT void set_setup_matrices(fptr *setup_matrices)
Definition: rxd.cpp:489
unsigned char _membrane_flux
Definition: rxd.cpp:109
int _rxd_euler_nnonzero
Definition: rxd.cpp:53
double * _rxd_c
Definition: rxd.cpp:60
long * _rxd_euler_nonzero_j
Definition: rxd.cpp:55
SpeciesIndexList * species_indices
Definition: rxd.cpp:72
int * _conc_indices
Definition: rxd.cpp:83
unsigned int * _rxd_zvi_child_count
Definition: rxd.cpp:63
NRN_EXPORT void set_setup(fptr *setup_fn)
Definition: rxd.cpp:480
double * _rxd_flux_scale
Definition: rxd.cpp:91
NRN_EXPORT void species_atolscale(int id, double scale, int len, int *idx)
Definition: rxd.cpp:1092
int _memb_curr_nodes
Definition: rxd.cpp:88
void apply_node_flux(int n, long *index, double *scale, PyObject **source, double dt, double *states)
Definition: rxd.cpp:314
long * _rxd_euler_nonzero_i
Definition: rxd.cpp:54
double * _rxd_euler_nonzero_values
Definition: rxd.cpp:56
static void add_currents(double *result)
Definition: rxd.cpp:443
int structure_change_cnt
Definition: treeset.cpp:66
fptr * _setup_units
Definition: rxd.cpp:48
double * t_ptr
Definition: grids.cpp:16
ECS_Grid_node ** _rxd_induced_currents_grid
Definition: rxd.cpp:107
int * _curr_indices
Definition: rxd.cpp:79
void do_ics_reactions(double *states, double *b, double *cvode_states, double *cvode_b)
Definition: rxd.cpp:1765
static void nrn_tree_solve(double *a, double *b, double *c, double *dbase, double *rhs, long *pindex, long n, double dt)
Definition: rxd.cpp:498
void _ode_reinit(double *y)
Definition: rxd.cpp:1330
static void ode_abs_tol(double *p1)
Definition: rxd.cpp:595
fptr * _setup_matrices
Definition: rxd.cpp:48
static void * allocopy(void *src, size_t size)
Definition: rxd.cpp:139
int _memb_count
Definition: rxd.cpp:90
NRN_EXPORT void clear_rates()
Definition: rxd.cpp:1015
int * _memb_species_count
Definition: rxd.cpp:96
TaskQueue * AllTasks
Definition: rxd.cpp:41
static void transfer_to_legacy()
Definition: rxd.cpp:131
NRN_EXPORT void rxd_setup_conc_ptrs(int conc_count, int *conc_index, PyHocObject **conc_ptrs)
Definition: rxd.cpp:198
static int _cvode_offset
Definition: rxd.cpp:65
int states_cvode_offset
long * _rxd_zero_volume_indices
Definition: rxd.cpp:57
unsigned char diffusion
Definition: rxd.cpp:52
NRN_EXPORT void set_setup_units(fptr *setup_units)
Definition: rxd.cpp:493
NrnThread * nrn_threads
Definition: rxd.cpp:49
double * states
Definition: rxd.cpp:75
int *** _memb_cur_mapped_ecs
Definition: rxd.cpp:104
void _rhs_variable_step(const double *p1, double *p2)
Definition: rxd.cpp:1349
NRN_EXPORT void rxd_include_node_flux1D(int n, long *index, double *scales, PyObject **sources)
Definition: rxd.cpp:296
NRN_EXPORT void free_curr_ptrs()
Definition: rxd.cpp:160
double * _rxd_induced_currents
Definition: rxd.cpp:106
void get_reaction_rates(ICSReactions *react, double *states, double *rates, double *ydot)
Definition: rxd.cpp:1446
NRN_EXPORT void rxd_set_euler_matrix(int nrow, int nnonzero, long *nonzero_i, long *nonzero_j, double *nonzero_values, double *c_diagonal)
Definition: rxd.cpp:358
double * _curr_scales
Definition: rxd.cpp:80
double * _node_flux_scale
Definition: rxd.cpp:114
unsigned int num_states
Definition: rxd.cpp:76
static void ode_solve(double, double *, double *)
Definition: rxd.cpp:556
void TaskQueue_add_task(TaskQueue *q, void *(*task)(void *), void *args, void *result)
Definition: rxd.cpp:1155
NRN_EXPORT void setup_currents(int num_currents, int num_fluxes, int *num_species, int *node_idxs, double *scales, PyHocObject **ptrs, int *mapped, int *mapped_ecs)
Definition: rxd.cpp:635
static int _ecs_count
Definition: rxd.cpp:66
static void apply_node_flux1D(double dt, double *states)
Definition: rxd.cpp:354
static void mul(int nnonzero, long *nonzero_i, long *nonzero_j, const double *nonzero_values, const double *v, double *result)
Definition: rxd.cpp:464
int _conc_count
Definition: rxd.cpp:82
ICSReactions * _reactions
Definition: rxd.cpp:69
int * _membrane_lookup
Definition: rxd.cpp:110
fptr * _initialize
Definition: rxd.cpp:48
std::vector< std::vector< neuron::container::data_handle< double > > > _memb_cur_ptrs
Definition: rxd.cpp:101
PyObject ** _node_flux_src
Definition: rxd.cpp:115
static void _currents(double *rhs)
Definition: rxd.cpp:745
void solve_reaction(ICSReactions *react, double *states, double *bval, double *cvode_states, double *cvode_b)
Definition: rxd.cpp:1537
int _curr_count
Definition: rxd.cpp:78
unsigned char initialized
Definition: rxd.cpp:28
long * _node_flux_idx
Definition: rxd.cpp:113
NRN_EXPORT void register_rate(int nspecies, int nparam, int nregions, int nseg, int *sidx, int necs, int necsparam, int *ecs_ids, int *ecsidx, int nmult, double *mult, PyHocObject **vptrs, ReactionRate *f)
Definition: rxd.cpp:857
long ** _rxd_zvi_child
Definition: rxd.cpp:64
int NUM_THREADS
Definition: rxd.cpp:34
long * _rxd_p
Definition: rxd.cpp:62
NRN_EXPORT void free_conc_ptrs()
Definition: rxd.cpp:171
void TaskQueue_sync(TaskQueue *q)
Definition: rxd.cpp:1270
double * _rxd_a
Definition: rxd.cpp:58
std::vector< neuron::container::data_handle< double > > _conc_ptrs
Definition: rxd.cpp:81
PyTypeObject * hocobject_type
Definition: nrnpy_hoc.cpp:136
void _fadvance(void)
Definition: rxd.cpp:1281
static void free_currents()
Definition: rxd.cpp:610
double * _rxd_b
Definition: rxd.cpp:59
fptr * _setup
Definition: rxd.cpp:48
NRN_EXPORT int rxd_nonvint_block(int method, int size, double *p1, double *p2, int)
Definition: rxd.cpp:777
int *** _memb_cur_mapped
Definition: rxd.cpp:103
void _rhs_variable_step_ecs(const double *, double *)
void ecs_atolscale(double *)
void _ecs_ode_reinit(double *)
void set_num_threads_3D(int n)
void clear_rates_ecs()
void ics_ode_solve(double, double *, const double *)
#define SPECIES_ABSENT
Definition: rxd.h:7
void(void) fptr
Definition: rxd.h:10
void _fadvance_fixed_step_3D(void)
#define NULL
Definition: spdefs.h:105
double ** flux
Definition: rxd.h:71
int num_segments
Definition: rxd.h:46
double ** result_array_dx
Definition: rxd.h:69
ReactionRate * reaction
Definition: rxd.h:42
struct ICSReactions * next
Definition: rxd.h:79
int num_species
Definition: rxd.h:43
double * ecs_states_for_reaction
Definition: rxd.h:73
double ** states_for_reaction_dx
Definition: rxd.h:66
int ** ecs_index
Definition: rxd.h:58
double ** params_for_reaction
Definition: rxd.h:67
int * ecs_offset_index
Definition: rxd.h:56
ECS_Grid_node ** ecs_grid
Definition: rxd.h:57
double ** result_array
Definition: rxd.h:68
double ** states_for_reaction
Definition: rxd.h:65
int icsN
Definition: rxd.h:48
int num_params
Definition: rxd.h:45
int num_ecs_params
Definition: rxd.h:54
int num_ecs_species
Definition: rxd.h:53
double * mc_mult
Definition: rxd.h:70
double ** vptrs
Definition: rxd.h:64
double * ecs_states_for_reaction_dx
Definition: rxd.h:74
int * ecsindex
Definition: rxd.h:78
double *** ecs_state
Definition: rxd.h:55
double * ecs_result_dx
Definition: rxd.h:77
double * ecs_result
Definition: rxd.h:76
int ecsN
Definition: rxd.h:59
int num_mult
Definition: rxd.h:61
double * ecs_params_for_reaction
Definition: rxd.h:75
double ** mc_multiplier
Definition: rxd.h:62
int num_regions
Definition: rxd.h:44
int *** state_idx
Definition: rxd.h:47
struct Item * next
Definition: model.h:12
Represent main neuron object computed by single thread.
Definition: multicore.h:58
double _dt
Definition: multicore.h:60
double _t
Definition: multicore.h:59
union PyHocObject::@37 u
neuron::container::data_handle< double > px_
int length
Definition: rxd.h:37
int * indices
Definition: rxd.h:36
struct SpeciesIndexList * next
Definition: rxd.h:38
double atolscale
Definition: rxd.h:35
Definition: rxd.h:82
Definition: rxd.h:89
std::mutex task_mutex
Definition: rxd.h:91
std::vector< bool > exit
Definition: rxd.h:92
std::condition_variable task_cond
Definition: rxd.h:90