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  }
618  free(_memb_cur_mapped[i]);
619  }
620  _memb_cur_ptrs.clear();
621  free(_memb_cur_mapped);
622  free(_memb_species_count);
623  free(_cur_node_indices);
624  free(_rxd_induced_currents);
625  free(_rxd_flux_scale);
626  free(_membrane_lookup);
627  free(_memb_cur_mapped_ecs);
631 }
632 
633 extern "C" NRN_EXPORT void setup_currents(int num_currents,
634  int num_fluxes,
635  int* num_species,
636  int* node_idxs,
637  double* scales,
638  PyHocObject** ptrs,
639  int* mapped,
640  int* mapped_ecs) {
641  int i, j, k, id, side, count;
642  int* induced_currents_ecs_idx;
643  int* induced_currents_grid_id;
644  int* ecs_indices;
645  double* current_scales;
646  PyHocObject** ecs_ptrs;
647 
648  Grid_node* g;
649  ECS_Grid_node* grid;
650 
651 
652  free_currents();
653 
654  _memb_count = num_currents;
655  _memb_curr_total = num_fluxes;
656  _memb_species_count = (int*) malloc(sizeof(int) * num_currents);
657  memcpy(_memb_species_count, num_species, sizeof(int) * num_currents);
658 
659  _rxd_flux_scale = (double*) calloc(sizeof(double), num_fluxes);
660  // memcpy(_rxd_flux_scale,scales,sizeof(double)*num_currents);
661 
662  /*TODO: if memory is an issue - replace with a map*/
663  _membrane_lookup = (int*) malloc(sizeof(int) * num_states);
664  memset(_membrane_lookup, SPECIES_ABSENT, sizeof(int) * num_states);
665 
666  _memb_cur_ptrs.resize(num_currents);
667  _memb_cur_mapped_ecs = (int***) malloc(sizeof(int*) * num_currents);
668  _memb_cur_mapped = (int***) malloc(sizeof(int**) * num_currents);
669  induced_currents_ecs_idx = (int*) malloc(sizeof(int) * _memb_curr_total);
670  induced_currents_grid_id = (int*) malloc(sizeof(int) * _memb_curr_total);
671  // initialize memory here to allow currents from an intracellular species
672  // with no corresponding nrn_region='o' or Extracellular species
673  memset(induced_currents_ecs_idx, SPECIES_ABSENT, sizeof(int) * _memb_curr_total);
674 
675  for (i = 0, k = 0; i < num_currents; i++) {
676  _memb_cur_ptrs[i].resize(num_species[i]);
677  _memb_cur_mapped_ecs[i] = (int**) malloc(sizeof(int*) * num_species[i]);
678  _memb_cur_mapped[i] = (int**) malloc(sizeof(int*) * num_species[i]);
679 
680 
681  for (j = 0; j < num_species[i]; j++, k++) {
682  _memb_cur_ptrs[i][j] = ptrs[k]->u.px_;
683  _memb_cur_mapped[i][j] = (int*) malloc(2 * sizeof(int));
684  _memb_cur_mapped_ecs[i][j] = (int*) malloc(2 * sizeof(int));
685 
686 
687  for (side = 0; side < 2; side++) {
688  _memb_cur_mapped[i][j][side] = mapped[2 * k + side];
689  _memb_cur_mapped_ecs[i][j][side] = mapped_ecs[2 * k + side];
690  }
691  for (side = 0; side < 2; side++) {
692  if (_memb_cur_mapped[i][j][side] != SPECIES_ABSENT) {
694  _rxd_flux_scale[k] = scales[i];
695  if (_memb_cur_mapped[i][j][(side + 1) % 2] == SPECIES_ABSENT) {
696  induced_currents_grid_id[k] = _memb_cur_mapped_ecs[i][j][0];
697  induced_currents_ecs_idx[k] = _memb_cur_mapped_ecs[i][j][1];
698  }
699  }
700  }
701  }
702  }
704  _rxd_induced_currents_scale = (double*) calloc(_memb_curr_total, sizeof(double));
705  for (id = 0, g = Parallel_grids[0]; g != NULL; g = g->next, id++) {
706  grid = dynamic_cast<ECS_Grid_node*>(g);
707  if (grid == NULL)
708  continue; // ignore ICS grids
709 
710  for (count = 0, k = 0; k < _memb_curr_total; k++) {
711  if (induced_currents_grid_id[k] == id) {
713  count++;
714  }
715  }
716  if (count > 0) {
717  ecs_indices = (int*) malloc(count * sizeof(int));
718  ecs_ptrs = (PyHocObject**) malloc(count * sizeof(PyHocObject*));
719  for (i = 0, k = 0; k < _memb_curr_total; k++) {
720  if (induced_currents_grid_id[k] == id) {
721  ecs_indices[i] = induced_currents_ecs_idx[k];
722  ecs_ptrs[i++] = ptrs[k];
723  }
724  }
725  current_scales = grid->set_rxd_currents(count, ecs_indices, ecs_ptrs);
726  free(ecs_ptrs);
727 
728  for (i = 0, k = 0; k < _memb_curr_total; k++) {
729  if (induced_currents_grid_id[k] == id)
730  _rxd_induced_currents_scale[k] = current_scales[i];
731  }
732  }
733  }
734  /*index into arrays of nodes/states*/
735  _cur_node_indices = (int*) malloc(sizeof(int) * num_currents);
736  memcpy(_cur_node_indices, node_idxs, sizeof(int) * num_currents);
738  _rxd_induced_currents = (double*) malloc(sizeof(double) * _memb_curr_total);
739  free(induced_currents_ecs_idx);
740  free(induced_currents_grid_id);
741 }
742 
743 static void _currents(double* rhs) {
744  int i, j, k, idx, side;
745  Grid_node* g;
746  ECS_Grid_node* grid;
747  if (!_membrane_flux)
748  return;
750  for (g = Parallel_grids[0]; g != NULL; g = g->next) {
751  grid = dynamic_cast<ECS_Grid_node*>(g);
752  if (grid)
753  grid->induced_idx = 0;
754  }
755  for (i = 0, k = 0; i < _memb_count; i++) {
756  idx = _cur_node_indices[i];
757 
758  for (j = 0; j < _memb_species_count[i]; j++, k++) {
759  rhs[idx] -= _rxd_induced_currents[k];
761 
762  for (side = 0; side < 2; side++) {
763  if (_memb_cur_mapped[i][j][side] == SPECIES_ABSENT) {
764  /*Extracellular region is within the ECS grid*/
766  if (grid != NULL && _memb_cur_mapped[i][j][(side + 1) % 2] != SPECIES_ABSENT)
767  grid->local_induced_currents[grid->induced_idx++] =
769  }
770  }
771  }
772  }
773 }
774 
775 extern "C" NRN_EXPORT int rxd_nonvint_block(int method, int size, double* p1, double* p2, int) {
776  if (initialized) {
778  /*TODO: Exclude irrelevant (non-rxd) structural changes*/
779  /*Needed for node.include_flux*/
780  _setup_matrices();
781  }
782  }
783  switch (method) {
784  case 0:
785  _setup();
786  break;
787  case 1:
788  _initialize();
789  // TODO: is there a better place to initialize multicompartment reactions
790  Grid_node* grid;
791  ECS_Grid_node* g;
792  for (grid = Parallel_grids[0]; grid != NULL; grid = grid->next) {
793  g = dynamic_cast<ECS_Grid_node*>(grid);
794  if (g)
796  }
797  break;
798  case 2:
799  /* compute outward current to be subtracted from rhs */
800  _currents(p1);
801  break;
802  case 3:
803  /* conductance to be added to d */
804  break;
805  case 4:
806  /* fixed step solve */
807  _fadvance();
809  break;
810  case 5:
811  /* ode_count */
812  _cvode_offset = size;
815  case 6:
816  /* ode_reinit(y) */
817  _ode_reinit(p1);
818  _ecs_ode_reinit(p1);
819  break;
820  case 7:
821  /* ode_fun(t, y, ydot); from t and y determine ydot */
822  _rhs_variable_step(p1, p2);
823  _rhs_variable_step_ecs(p1, p2);
824  break;
825  case 8:
826  ode_solve(*dt_ptr, p1, p2); /*solve mx=b replace b with x */
827  /* TODO: we can probably reuse the dgadi code here... for now, we do nothing, which
828  * implicitly approximates the Jacobian as the identity matrix */
829  // y= p1 = states and b = p2 = RHS for x direction
830  ics_ode_solve(*dt_ptr, p1, p2);
831  break;
832  case 9:
833  // ode_jacobian(*dt_ptr, p1, p2); /* optionally prepare jacobian for fast ode_solve */
834  break;
835  case 10:
836  ode_abs_tol(p1);
837  ecs_atolscale(p1);
838  /* ode_abs_tol(y_abs_tolerance); fill with cvode.atol() * scalefactor */
839  break;
840  default:
841  printf("Unknown rxd_nonvint_block call: %d\n", method);
842  break;
843  }
844  return 0;
845 }
846 
847 
848 /*****************************************************************************
849  *
850  * Begin intracellular code
851  *
852  *****************************************************************************/
853 
854 
855 extern "C" NRN_EXPORT void register_rate(int nspecies,
856  int nparam,
857  int nregions,
858  int nseg,
859  int* sidx,
860  int necs,
861  int necsparam,
862  int* ecs_ids,
863  int* ecsidx,
864  int nmult,
865  double* mult,
866  PyHocObject** vptrs,
867  ReactionRate* f) {
868  int i, j, k, idx, ecs_id, ecs_index, ecs_offset;
869  unsigned char counted;
870  Grid_node* g;
871  ECS_Grid_node* grid;
872  ICSReactions* react = (ICSReactions*) malloc(sizeof(ICSReactions));
873  react->reaction = f;
874  react->num_species = nspecies;
875  react->num_regions = nregions;
876  react->num_params = nparam;
877  react->num_segments = nseg;
878  react->num_ecs_species = necs;
879  react->num_ecs_params = necsparam;
880  react->num_mult = nmult;
881  react->icsN = 0;
882  react->ecsN = 0;
883  if (vptrs != NULL) {
884  react->vptrs = (double**) malloc(nseg * sizeof(double*));
885  for (i = 0; i < nseg; i++)
886  react->vptrs[i] = static_cast<double*>(vptrs[i]->u.px_);
887  } else {
888  react->vptrs = NULL;
889  }
890  react->state_idx = (int***) malloc(nseg * sizeof(int**));
891  for (i = 0, idx = 0; i < nseg; i++) {
892  react->state_idx[i] = (int**) malloc((nspecies + nparam) * sizeof(int*));
893  for (j = 0; j < nspecies + nparam; j++) {
894  react->state_idx[i][j] = (int*) malloc(nregions * sizeof(int));
895  for (k = 0; k < nregions; k++, idx++) {
896  if (sidx[idx] < 0) {
897  react->state_idx[i][j][k] = SPECIES_ABSENT;
898  } else {
899  react->state_idx[i][j][k] = sidx[idx];
900  if (i == 0 && j < nspecies)
901  react->icsN++;
902  }
903  }
904  }
905  }
906  if (nmult > 0) {
907  react->mc_multiplier = (double**) malloc(nmult * sizeof(double*));
908  for (i = 0; i < nmult; i++) {
909  react->mc_multiplier[i] = (double*) malloc(nseg * sizeof(double));
910  memcpy(react->mc_multiplier[i], (mult + i * nseg), nseg * sizeof(double));
911  }
912  }
913 
914  if (react->num_ecs_species + react->num_ecs_params > 0) {
915  react->ecs_grid = (ECS_Grid_node**) malloc(react->num_ecs_species * sizeof(Grid_node*));
916  react->ecs_state = (double***) malloc(nseg * sizeof(double**));
917  react->ecs_index = (int**) malloc(nseg * sizeof(int*));
918  react->ecs_offset_index = (int*) malloc(react->num_ecs_species * sizeof(int));
919  for (i = 0; i < nseg; i++) {
920  react->ecs_state[i] = (double**) malloc((necs + necsparam) * sizeof(double*));
921  react->ecs_index[i] = (int*) malloc((necs + necsparam) * sizeof(int));
922  }
923  for (j = 0; j < necs + necsparam; j++) {
924  ecs_offset = num_states - _rxd_num_zvi;
925 
926  for (ecs_id = 0, g = Parallel_grids[0]; g != NULL; g = g->next, ecs_id++) {
927  if (ecs_id == ecs_ids[j]) {
928  grid = dynamic_cast<ECS_Grid_node*>(g);
929  assert(grid != NULL);
930  if (j < necs) {
931  react->ecs_grid[j] = grid;
932  react->ecs_offset_index[j] =
933  grid->add_multicompartment_reaction(nseg, &ecsidx[j], necs + necsparam);
934  }
935 
936  for (i = 0, counted = FALSE; i < nseg; i++) {
937  // nseg x nregion x nspecies
938  ecs_index = ecsidx[i * (necs + necsparam) + j];
939 
940  if (ecs_index >= 0) {
941  react->ecs_state[i][j] = &(grid->states[ecs_index]);
942  react->ecs_index[i][j] = ecs_offset + ecs_index;
943  if (j < necs && !counted) {
944  react->ecsN++;
945  counted = TRUE;
946  }
947  } else {
948  react->ecs_state[i][j] = NULL;
949  }
950  }
951  ecs_offset += grid->size_x * grid->size_y * grid->size_z;
952  }
953  }
954  }
955  } else {
956  react->ecs_state = NULL;
957  }
958  if (react->num_mult > 0)
959  react->mc_mult = (double*) malloc(react->num_mult * sizeof(double));
960 
961  if (react->num_ecs_species > 0) {
962  react->ecs_states_for_reaction = (double*) malloc(react->num_ecs_species * sizeof(double));
963  react->ecs_states_for_reaction_dx = (double*) malloc(react->num_ecs_species *
964  sizeof(double));
965  react->ecs_result = (double*) malloc(react->num_ecs_species * sizeof(double));
966  react->ecs_result_dx = (double*) malloc(react->num_ecs_species * sizeof(double));
967  react->ecsindex = (int*) malloc(react->num_ecs_species * sizeof(int));
968  }
969  if (react->num_ecs_params > 0)
970  react->ecs_params_for_reaction = (double*) calloc(react->num_ecs_params, sizeof(double));
971 
972  if (_membrane_flux) {
973  react->flux = (double**) malloc(react->icsN * sizeof(double*));
974  for (i = 0; i < react->icsN; i++)
975  react->flux[i] = (double*) malloc(react->num_regions * sizeof(double));
976  }
977  react->states_for_reaction = (double**) malloc(react->num_species * sizeof(double*));
978  react->states_for_reaction_dx = (double**) malloc(react->num_species * sizeof(double*));
979 
980  react->result_array = (double**) malloc(react->num_species * sizeof(double*));
981  react->result_array_dx = (double**) malloc(react->num_species * sizeof(double*));
982 
983  for (i = 0; i < react->num_species; i++) {
984  react->states_for_reaction[i] = (double*) malloc(react->num_regions * sizeof(double));
985  react->states_for_reaction_dx[i] = (double*) malloc(react->num_regions * sizeof(double));
986 
987  react->result_array[i] = (double*) malloc(react->num_regions * sizeof(double));
988  react->result_array_dx[i] = (double*) malloc(react->num_regions * sizeof(double));
989  }
990 
991  react->params_for_reaction = (double**) malloc(react->num_params * sizeof(double*));
992  for (i = 0; i < react->num_params; i++)
993  react->params_for_reaction[i] = (double*) malloc(react->num_regions * sizeof(double));
994  if (react->num_mult > 0)
995  react->mc_mult = (double*) malloc(react->num_mult * sizeof(double));
996 
997  if (_reactions == NULL) {
998  _reactions = react;
999  react->next = NULL;
1000 
1001  } else {
1002  react->next = _reactions;
1003  _reactions = react;
1004  }
1005 
1006  for (g = Parallel_grids[0]; g != NULL; g = g->next) {
1007  grid = dynamic_cast<ECS_Grid_node*>(g);
1008  if (grid)
1010  }
1011 }
1012 
1013 extern "C" NRN_EXPORT void clear_rates() {
1014  ICSReactions *react, *prev;
1015  int i, j;
1016  for (react = _reactions; react != NULL;) {
1017  if (react->vptrs != NULL)
1018  free(react->vptrs);
1019  for (i = 0; i < react->num_segments; i++) {
1020  for (j = 0; j < react->num_species; j++) {
1021  free(react->state_idx[i][j]);
1022  }
1023  free(react->state_idx[i]);
1024 
1025  if (react->num_ecs_species + react->num_ecs_params > 0) {
1026  free(react->ecs_state[i]);
1027  }
1028  }
1029  if (react->num_mult > 0) {
1030  for (i = 0; i < react->num_mult; i++)
1031  free(react->mc_multiplier[i]);
1032  free(react->mc_multiplier);
1033  }
1034 
1035  free(react->state_idx);
1036  free(react->ecs_state);
1037  prev = react;
1038 
1039  if (react->num_mult > 0)
1040  free(react->mc_mult);
1041  if (_membrane_flux) {
1042  for (i = 0; i < react->icsN; i++)
1043  free(react->flux[i]);
1044  free(react->flux);
1045  }
1046  if (react->num_ecs_species > 0) {
1047  free(react->ecs_states_for_reaction);
1048  free(react->ecs_states_for_reaction_dx);
1049  free(react->ecs_result);
1050  free(react->ecs_result_dx);
1051  free(react->ecsindex);
1052  }
1053  for (i = 0; i < react->num_species; i++) {
1054  free(react->states_for_reaction[i]);
1055  free(react->result_array[i]);
1056  }
1057  free(react->states_for_reaction);
1058  free(react->result_array);
1059  for (i = 0; i < react->num_params; i++) {
1060  free(react->params_for_reaction[i]);
1061  }
1062  free(react->params_for_reaction);
1063  if (react->num_ecs_params > 0) {
1064  free(react->ecs_params_for_reaction);
1065  }
1066  react = react->next;
1067  free(prev);
1068  }
1069  _reactions = NULL;
1070  /*clear extracellular reactions*/
1071  clear_rates_ecs();
1072  // There are NUM_THREADS-1 std::thread objects alive, and these need to be
1073  // cleaned up before exit (otherwise the std::thread destructor will call
1074  // std::terminate.).
1075  set_num_threads(1);
1076 }
1077 
1078 
1079 extern "C" NRN_EXPORT void species_atolscale(int id, double scale, int len, int* idx) {
1080  SpeciesIndexList* list;
1082  if (species_indices != NULL) {
1083  for (list = species_indices, prev = NULL; list != NULL; list = list->next) {
1084  if (list->id == id) {
1085  list->atolscale = scale;
1086  return;
1087  }
1088  prev = list;
1089  }
1090  prev->next = (SpeciesIndexList*) malloc(sizeof(SpeciesIndexList));
1091  list = prev->next;
1092  } else {
1093  species_indices = (SpeciesIndexList*) malloc(sizeof(SpeciesIndexList));
1094  list = species_indices;
1095  }
1096  list->id = id;
1097  list->indices = (int*) malloc(sizeof(int) * len);
1098  memcpy(list->indices, idx, sizeof(int) * len);
1099  list->length = len;
1100  list->atolscale = scale;
1101  list->next = NULL;
1102 }
1103 
1104 extern "C" NRN_EXPORT void remove_species_atolscale(int id) {
1105  SpeciesIndexList* list;
1107  for (list = species_indices, prev = NULL; list != NULL; prev = list, list = list->next) {
1108  if (list->id == id) {
1109  if (prev == NULL)
1110  species_indices = list->next;
1111  else
1112  prev->next = list->next;
1113  free(list->indices);
1114  free(list);
1115  break;
1116  }
1117  }
1118 }
1119 
1120 extern "C" NRN_EXPORT void setup_solver(double* my_states,
1121  int my_num_states,
1122  long* zvi,
1123  int num_zvi) {
1124  free_currents();
1125  states = my_states;
1126  num_states = my_num_states;
1127  free_zvi_child();
1128  _rxd_num_zvi = num_zvi;
1131  if (num_zvi == 0)
1133  else
1134  _rxd_zero_volume_indices = (long*) allocopy(zvi, num_zvi * sizeof(long));
1135  dt_ptr = &nrn_threads->_dt;
1136  t_ptr = &nrn_threads->_t;
1138  initialized = TRUE;
1140 }
1141 
1142 void TaskQueue_add_task(TaskQueue* q, void* (*task)(void*), void* args, void* result) {
1143  auto* t = new TaskList{};
1144  t->task = task;
1145  t->args = args;
1146  t->result = result;
1147  t->next = nullptr;
1148 
1149  // Add task to the queue
1150  {
1151  std::lock_guard<std::mutex> _{q->task_mutex};
1152  if (!q->first) {
1153  // empty queue
1154  q->first = t;
1155  q->last = t;
1156  } else {
1157  // queue not empty
1158  q->last->next = t;
1159  q->last = t;
1160  }
1161  {
1162  std::lock_guard<std::mutex> _{q->waiting_mutex};
1163  ++q->length;
1164  }
1165  }
1166 
1167  // signal a waiting thread that there is a new task to pick up
1168  q->task_cond.notify_one();
1169 }
1170 
1171 void TaskQueue_exe_tasks(std::size_t thread_index, TaskQueue* q) {
1172  for (;;) {
1173  // Wait for a task to be available in the queue, then execute it.
1174  {
1175  TaskList* job{};
1176  {
1177  std::unique_lock<std::mutex> lock{q->task_mutex};
1178  // Wait until either for a new task to be received or for this
1179  // thread to be told to exit.
1180  q->task_cond.wait(lock,
1181  [q, thread_index] { return q->first || q->exit[thread_index]; });
1182  if (q->exit[thread_index]) {
1183  return;
1184  }
1185  job = q->first;
1186  q->first = job->next;
1187  }
1188  // Execute the task
1189  job->result = job->task(job->args);
1190  delete job;
1191  }
1192  // Decrement the list length, if it's now empty then broadcast that to
1193  // the master thread, which may be waiting for the queue to be empty.
1194  auto const new_length = [q] {
1195  std::lock_guard<std::mutex> _{q->waiting_mutex};
1196  return --(q->length);
1197  }();
1198  // The immediately-executed lambda means we release the mutex before
1199  // calling notify_one()
1200  if (new_length == 0) {
1201  // Queue is empty. Notify the main thread, which may be blocking on
1202  // this condition.
1203  q->waiting_cond.notify_one();
1204  }
1205  }
1206 }
1207 
1208 
1209 extern "C" NRN_EXPORT void set_num_threads(const int n) {
1210  assert(n > 0);
1211  assert(NUM_THREADS > 0);
1212  // n and NUM_THREADS include the main thread, old_num and new_num refer to
1213  // the number of std::thread workers
1214  std::size_t const old_num = NUM_THREADS - 1;
1215  std::size_t const new_num = n - 1;
1216  assert(old_num == Threads.size());
1217  assert(old_num == task_queue.exit.size());
1218  if (new_num < old_num) {
1219  // Kill some threads. First, wait until the queue is empty.
1221  // Now signal to the threads that are to be killed that they need to
1222  // exit.
1223  {
1224  std::lock_guard<std::mutex> _{task_queue.task_mutex};
1225  for (auto k = new_num; k < old_num; ++k) {
1226  task_queue.exit[k] = true;
1227  }
1228  }
1229  task_queue.task_cond.notify_all();
1230  // Finally, join those threads and destroy the std::thread objects.
1231  for (auto k = new_num; k < old_num; ++k) {
1232  Threads[k].join();
1233  }
1234  // And update the structures
1235  {
1236  std::lock_guard<std::mutex> _{task_queue.task_mutex};
1237  Threads.resize(new_num);
1238  task_queue.exit.resize(new_num);
1239  }
1240  } else if (new_num > old_num) {
1241  // Create some threads
1242  std::lock_guard<std::mutex> _{task_queue.task_mutex};
1243  task_queue.exit.reserve(new_num);
1244  Threads.reserve(new_num);
1245  for (auto k = old_num; k < new_num; ++k) {
1246  assert(k == Threads.size());
1247  Threads.emplace_back(TaskQueue_exe_tasks, k, &task_queue);
1248  task_queue.exit.emplace_back(false);
1249  }
1250  }
1251  assert(new_num == Threads.size());
1252  assert(new_num == task_queue.exit.size());
1254  NUM_THREADS = n;
1255 }
1256 
1258  // Wait till the queue is empty
1259  std::unique_lock<std::mutex> lock{q->waiting_mutex};
1260  q->waiting_cond.wait(lock, [q] { return q->length == 0; });
1261 }
1262 
1263 extern "C" NRN_EXPORT int get_num_threads(void) {
1264  return NUM_THREADS;
1265 }
1266 
1267 
1268 void _fadvance(void) {
1269  double dt = *dt_ptr;
1270  long i;
1271  /*variables for diffusion*/
1272  double* rhs;
1273  long* zvi = _rxd_zero_volume_indices;
1274 
1275  rhs = (double*) calloc(num_states, sizeof(double));
1276  /*diffusion*/
1277  if (diffusion)
1282  states,
1283  rhs);
1284 
1285  add_currents(rhs);
1286 
1287  /* multiply rhs vector by dt */
1288  for (i = 0; i < num_states; i++) {
1289  rhs[i] *= dt;
1290  }
1291 
1292  if (diffusion)
1294 
1295  /* increment states by rhs which is now really deltas */
1296  for (i = 0; i < num_states; i++) {
1297  states[i] += rhs[i];
1298  }
1299 
1300  /* clear zero volume indices (conservation nodes) */
1301  for (i = 0; i < _rxd_num_zvi; i++) {
1302  states[zvi[i]] = 0;
1303  }
1304 
1305  free(rhs);
1306 
1307  /*reactions*/
1309 
1310  /*node fluxes*/
1312 
1314 }
1315 
1316 
1317 void _ode_reinit(double* y) {
1318  long i, j;
1319  long* zvi = _rxd_zero_volume_indices;
1320  y += _cvode_offset;
1321  /*Copy states to CVode*/
1322  if (_rxd_num_zvi > 0) {
1323  for (i = 0, j = 0; i < num_states; i++) {
1324  if (zvi[j] == i)
1325  j++;
1326  else {
1327  y[i - j] = states[i];
1328  }
1329  }
1330  } else {
1331  memcpy(y, states, sizeof(double) * num_states);
1332  }
1333 }
1334 
1335 
1336 void _rhs_variable_step(const double* p1, double* p2) {
1337  Grid_node* grid;
1338  long i, j, p, c;
1339  unsigned int k;
1340  const unsigned char calculate_rhs = p2 == NULL ? 0 : 1;
1341  const double* my_states = p1 + _cvode_offset;
1342  double* ydot = p2 + _cvode_offset;
1343  /*variables for diffusion*/
1344  double* rhs;
1345  long* zvi = _rxd_zero_volume_indices;
1346 
1347  double const* const orig_states3d = p1 + states_cvode_offset;
1348  double* const orig_ydot3d = p2 + states_cvode_offset;
1349 
1350  /*Copy states from CVode*/
1351  if (_rxd_num_zvi > 0) {
1352  for (i = 0, j = 0; i < num_states; i++) {
1353  if (zvi[j] == i)
1354  j++;
1355  else {
1356  states[i] = my_states[i - j];
1357  }
1358  }
1359  } else {
1360  memcpy(states, my_states, sizeof(double) * num_states);
1361  }
1362 
1363  if (diffusion) {
1364  for (i = 0; i < _rxd_num_zvi; i++) {
1365  j = zvi[i];
1366  p = _rxd_p[j];
1367  states[j] = (p > 0 ? -(_rxd_b[j] / _rxd_d[j]) * states[p] : 0);
1368  for (k = 0; k < _rxd_zvi_child_count[i]; k++) {
1369  c = _rxd_zvi_child[i][k];
1370  states[j] -= (_rxd_a[c] / _rxd_d[j]) * states[c];
1371  }
1372  }
1373  }
1374 
1376 
1377  if (!calculate_rhs) {
1378  for (i = 0; i < _rxd_num_zvi; i++)
1379  states[zvi[i]] = 0;
1380  return;
1381  }
1382 
1383  /*diffusion*/
1384  rhs = (double*) calloc(num_states, sizeof(double));
1385 
1386  if (diffusion)
1391  states,
1392  rhs);
1393 
1394  /*reactions*/
1395  memset(&ydot[num_states - _rxd_num_zvi], 0, sizeof(double) * _ecs_count);
1397 
1398 
1399  const double* states3d = orig_states3d;
1400  double* ydot3d = orig_ydot3d;
1401  int grid_size;
1402  for (grid = Parallel_grids[0]; grid != NULL; grid = grid->next) {
1403  grid_size = grid->size_x * grid->size_y * grid->size_z;
1404  if (grid->hybrid) {
1405  grid->variable_step_hybrid_connections(states3d, ydot3d, states, rhs);
1406  }
1407  ydot3d += grid_size;
1408  states3d += grid_size;
1409  }
1410 
1411  /*Add currents to the result*/
1412  add_currents(rhs);
1413 
1414  /*Add node fluxes to the result*/
1415  apply_node_flux1D(1.0, rhs);
1416 
1417  /* increment states by rhs which is now really deltas */
1418  if (_rxd_num_zvi > 0) {
1419  for (i = 0, j = 0; i < num_states; i++) {
1420  if (zvi[j] == i) {
1421  states[i] = 0;
1422  j++;
1423  } else {
1424  ydot[i - j] = rhs[i];
1425  }
1426  }
1427  } else {
1428  memcpy(ydot, rhs, sizeof(double) * num_states);
1429  }
1430  free(rhs);
1431 }
1432 
1433 void get_reaction_rates(ICSReactions* react, double* states, double* rates, double* ydot) {
1434  int segment;
1435  int i, j, k, idx;
1436  double v = 0;
1437  for (i = 0; i < react->num_ecs_species; i++)
1438  react->ecsindex[i] = react->ecs_grid[i]->react_offsets[react->ecs_offset_index[i]];
1439 
1440  if (_membrane_flux) {
1441  for (i = 0; i < react->icsN; i++)
1442  memset(react->flux[i], 0, react->num_regions * sizeof(double));
1443  }
1444  for (segment = 0; segment < react->num_segments; segment++) {
1445  for (i = 0; i < react->num_species; i++) {
1446  for (j = 0; j < react->num_regions; j++) {
1447  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1448  react->states_for_reaction[i][j] = states[react->state_idx[segment][i][j]];
1449  } else {
1450  react->states_for_reaction[i][j] = NAN;
1451  }
1452  }
1453  memset(react->result_array[i], 0, react->num_regions * sizeof(double));
1454  }
1455  for (k = 0; i < react->num_species + react->num_params; i++, k++) {
1456  for (j = 0; j < react->num_regions; j++) {
1457  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1458  react->params_for_reaction[k][j] = states[react->state_idx[segment][i][j]];
1459  } else {
1460  react->params_for_reaction[k][j] = NAN;
1461  }
1462  }
1463  }
1464 
1465  for (i = 0; i < react->num_ecs_species; i++) {
1466  if (react->ecs_state[segment][i] != NULL) {
1467  react->ecs_states_for_reaction[i] = *(react->ecs_state[segment][i]);
1468  } else {
1469  react->ecs_states_for_reaction[i] = NAN;
1470  }
1471  }
1472  for (k = 0; i < react->num_ecs_species + react->num_ecs_params; i++, k++) {
1473  if (react->ecs_state[segment][i] != NULL) {
1474  react->ecs_params_for_reaction[k] = *(react->ecs_state[segment][i]);
1475  } else {
1476  react->ecs_params_for_reaction[k] = NAN;
1477  }
1478  }
1479  memset(react->ecs_result, 0, react->num_ecs_species * sizeof(double));
1480 
1481  for (i = 0; i < react->num_mult; i++) {
1482  react->mc_mult[i] = react->mc_multiplier[i][segment];
1483  }
1484 
1485  if (react->vptrs != NULL) {
1486  v = *(react->vptrs[segment]);
1487  }
1488 
1489  react->reaction(react->states_for_reaction,
1490  react->params_for_reaction,
1491  react->result_array,
1492  react->mc_mult,
1493  react->ecs_states_for_reaction,
1494  react->ecs_params_for_reaction,
1495  react->ecs_result,
1496  react->flux,
1497  v);
1498 
1499  for (i = 0; i < react->num_species; i++) {
1500  for (j = 0; j < react->num_regions; j++) {
1501  idx = react->state_idx[segment][i][j];
1502  if (idx != SPECIES_ABSENT) {
1505  _rxd_flux_scale[_membrane_lookup[idx]] * react->flux[i][j];
1506  }
1507  if (rates != NULL) {
1508  rates[idx] += react->result_array[i][j];
1509  }
1510  }
1511  }
1512  }
1513  if (ydot != NULL) {
1514  for (i = 0; i < react->num_ecs_species; i++) {
1515  if (react->ecs_state[segment][i] != NULL)
1516  react->ecs_grid[i]->all_reaction_states[react->ecsindex[i]++] =
1517  react->ecs_result[i];
1518  // ydot[react->ecs_index[segment][i]] += ecs_result[i];
1519  }
1520  }
1521  }
1522 }
1523 
1525  double* states,
1526  double* bval,
1527  double* cvode_states,
1528  double* cvode_b) {
1529  int segment;
1530  int i, j, k, idx, jac_i, jac_j, jac_idx;
1531  int N = react->icsN + react->ecsN; /*size of Jacobian (number species*regions for a segments)*/
1532  double pd;
1533  double dt = *dt_ptr;
1534  double dx = FLT_EPSILON;
1535  OcFullMatrix jacobian(N, N);
1536  auto b = std::make_unique<IvocVect>(N);
1537  auto x = std::make_unique<IvocVect>(N);
1538  double v = 0;
1539 
1540  if (react->num_ecs_species > 0) {
1541  for (i = 0; i < react->num_ecs_species; i++)
1542  react->ecsindex[i] = react->ecs_grid[i]->react_offsets[react->ecs_offset_index[i]];
1543  }
1544 
1545  for (segment = 0; segment < react->num_segments; segment++) {
1546  if (react->vptrs != NULL)
1547  v = *(react->vptrs[segment]);
1548 
1549  for (i = 0; i < react->num_species; i++) {
1550  for (j = 0; j < react->num_regions; j++) {
1551  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1552  react->states_for_reaction[i][j] = states[react->state_idx[segment][i][j]];
1553  react->states_for_reaction_dx[i][j] = react->states_for_reaction[i][j];
1554  } else {
1556  react->states_for_reaction_dx[i][j] = react->states_for_reaction[i][j];
1557  }
1558  }
1559  memset(react->result_array[i], 0, react->num_regions * sizeof(double));
1560  memset(react->result_array_dx[i], 0, react->num_regions * sizeof(double));
1561  }
1562  for (k = 0; i < react->num_species + react->num_params; i++, k++) {
1563  for (j = 0; j < react->num_regions; j++) {
1564  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1565  react->params_for_reaction[k][j] = states[react->state_idx[segment][i][j]];
1566  } else {
1568  }
1569  }
1570  }
1571 
1572 
1573  for (i = 0; i < react->num_ecs_species; i++) {
1574  if (react->ecs_state[segment][i] != NULL) {
1575  if (cvode_states != NULL) {
1576  react->ecs_states_for_reaction[i] = cvode_states[react->ecs_index[segment][i]];
1577  } else {
1578  react->ecs_states_for_reaction[i] = *(react->ecs_state[segment][i]);
1579  }
1581  }
1582  }
1583  for (k = 0; i < react->num_ecs_species + react->num_ecs_params; i++, k++) {
1584  if (react->ecs_state[segment][i] != NULL) {
1585  react->ecs_params_for_reaction[k] = *(react->ecs_state[segment][i]);
1586  }
1587  }
1588 
1589  if (react->num_ecs_species > 0) {
1590  memset(react->ecs_result, 0, react->num_ecs_species * sizeof(double));
1591  memset(react->ecs_result_dx, 0, react->num_ecs_species * sizeof(double));
1592  }
1593 
1594  for (i = 0; i < react->num_mult; i++) {
1595  react->mc_mult[i] = react->mc_multiplier[i][segment];
1596  }
1597 
1598  react->reaction(react->states_for_reaction,
1599  react->params_for_reaction,
1600  react->result_array,
1601  react->mc_mult,
1602  react->ecs_states_for_reaction,
1603  react->ecs_params_for_reaction,
1604  react->ecs_result,
1605  NULL,
1606  v);
1607 
1608  /*Calculate I - Jacobian for ICS reactions*/
1609  for (i = 0, idx = 0; i < react->num_species; i++) {
1610  for (j = 0; j < react->num_regions; j++) {
1611  if (react->state_idx[segment][i][j] != SPECIES_ABSENT) {
1612  if (bval == NULL)
1613  b->elem(idx) = dt * react->result_array[i][j];
1614  else
1615  b->elem(idx) = bval[react->state_idx[segment][i][j]];
1616 
1617 
1618  // set up the changed states array
1619  react->states_for_reaction_dx[i][j] += dx;
1620 
1621  /* TODO: Handle approximating the Jacobian at a function upper
1622  * limit, e.g. acos(1)
1623  */
1624  react->reaction(react->states_for_reaction_dx,
1625  react->params_for_reaction,
1626  react->result_array_dx,
1627  react->mc_mult,
1628  react->ecs_states_for_reaction,
1629  react->ecs_params_for_reaction,
1630  react->ecs_result_dx,
1631  NULL,
1632  v);
1633 
1634  for (jac_i = 0, jac_idx = 0; jac_i < react->num_species; jac_i++) {
1635  for (jac_j = 0; jac_j < react->num_regions; jac_j++) {
1636  // pd is our Jacobian approximated
1637  if (react->state_idx[segment][jac_i][jac_j] != SPECIES_ABSENT) {
1638  pd = (react->result_array_dx[jac_i][jac_j] -
1639  react->result_array[jac_i][jac_j]) /
1640  dx;
1641  jacobian(jac_idx, idx) = (idx == jac_idx) - dt * pd;
1642  jac_idx += 1;
1643  }
1644  react->result_array_dx[jac_i][jac_j] = 0;
1645  }
1646  }
1647  for (jac_i = 0; jac_i < react->num_ecs_species; jac_i++) {
1648  // pd is our Jacobian approximated
1649  if (react->ecs_state[segment][jac_i] != NULL) {
1650  pd = (react->ecs_result_dx[jac_i] - react->ecs_result[jac_i]) / dx;
1651  jacobian(jac_idx, idx) = -dt * pd;
1652  jac_idx += 1;
1653  }
1654  react->ecs_result_dx[jac_i] = 0;
1655  }
1656  // reset dx array
1657  react->states_for_reaction_dx[i][j] -= dx;
1658  idx++;
1659  }
1660  }
1661  }
1662 
1663  /*Calculate I - Jacobian for MultiCompartment ECS reactions*/
1664  for (i = 0; i < react->num_ecs_species; i++) {
1665  if (react->ecs_state[segment][i] != NULL) {
1666  if (bval == NULL)
1667  b->elem(idx) = dt * react->ecs_result[i];
1668  else
1669  b->elem(idx) = cvode_b[react->ecs_index[segment][i]];
1670 
1671 
1672  // set up the changed states array
1673  react->ecs_states_for_reaction_dx[i] += dx;
1674 
1675  /* TODO: Handle approximating the Jacobian at a function upper
1676  * limit, e.g. acos(1)
1677  */
1678  react->reaction(react->states_for_reaction,
1679  react->params_for_reaction,
1680  react->result_array_dx,
1681  react->mc_mult,
1683  react->ecs_params_for_reaction,
1684  react->ecs_result_dx,
1685  NULL,
1686  v);
1687 
1688  for (jac_i = 0, jac_idx = 0; jac_i < react->num_species; jac_i++) {
1689  for (jac_j = 0; jac_j < react->num_regions; jac_j++) {
1690  // pd is our Jacobian approximated
1691  if (react->state_idx[segment][jac_i][jac_j] != SPECIES_ABSENT) {
1692  pd = (react->result_array_dx[jac_i][jac_j] -
1693  react->result_array[jac_i][jac_j]) /
1694  dx;
1695  jacobian(jac_idx, idx) = -dt * pd;
1696  jac_idx += 1;
1697  }
1698  }
1699  }
1700  for (jac_i = 0; jac_i < react->num_ecs_species; jac_i++) {
1701  // pd is our Jacobian approximated
1702  if (react->ecs_state[segment][jac_i] != NULL) {
1703  pd = (react->ecs_result_dx[jac_i] - react->ecs_result[jac_i]) / dx;
1704  jacobian(jac_idx, idx) = (idx == jac_idx) - dt * pd;
1705  jac_idx += 1;
1706  } else {
1707  jacobian(idx, idx) = 1.0;
1708  }
1709  // reset dx array
1710  react->ecs_states_for_reaction_dx[i] -= dx;
1711  }
1712  idx++;
1713  }
1714  }
1715  // solve for x, destructively
1716  jacobian.solv(b.get(), x.get(), false);
1717 
1718  if (bval != NULL) // variable-step
1719  {
1720  for (i = 0, jac_idx = 0; i < react->num_species; i++) {
1721  for (j = 0; j < react->num_regions; j++) {
1722  idx = react->state_idx[segment][i][j];
1723  if (idx != SPECIES_ABSENT) {
1724  bval[idx] = x->elem(jac_idx++);
1725  }
1726  }
1727  }
1728  for (i = 0; i < react->num_ecs_species; i++) {
1729  if (react->ecs_state[segment][i] != NULL)
1730  react->ecs_grid[i]->all_reaction_states[react->ecsindex[i]++] = x->elem(
1731  jac_idx++);
1732  // cvode_b[react->ecs_index[segment][i]] = x->elem(jac_idx++);
1733  }
1734  } else // fixed-step
1735  {
1736  for (i = 0, jac_idx = 0; i < react->num_species; i++) {
1737  for (j = 0; j < react->num_regions; j++) {
1738  idx = react->state_idx[segment][i][j];
1739  if (idx != SPECIES_ABSENT)
1740  states[idx] += x->elem(jac_idx++);
1741  }
1742  }
1743  for (i = 0; i < react->num_ecs_species; i++) {
1744  if (react->ecs_state[segment][i] != NULL)
1745  react->ecs_grid[i]->all_reaction_states[react->ecsindex[i]++] = x->elem(
1746  jac_idx++);
1747  }
1748  }
1749  }
1750 }
1751 
1752 void do_ics_reactions(double* states, double* b, double* cvode_states, double* cvode_b) {
1753  ICSReactions* react;
1754  for (react = _reactions; react != NULL; react = react->next) {
1755  if (react->icsN + react->ecsN > 0)
1756  solve_reaction(react, states, b, cvode_states, cvode_b);
1757  }
1758 }
1759 
1760 void get_all_reaction_rates(double* states, double* rates, double* ydot) {
1761  ICSReactions* react;
1762  if (_membrane_flux)
1763  memset(_rxd_induced_currents, 0, sizeof(double) * _memb_curr_total);
1764  for (react = _reactions; react != NULL; react = react->next) {
1765  if (react->icsN + react->ecsN > 0)
1766  get_reaction_rates(react, states, rates, ydot);
1767  }
1768 }
1769 
1770 /*****************************************************************************
1771  *
1772  * End intracellular code
1773  *
1774  *****************************************************************************/
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:1111
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:1120
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:1263
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:1171
void get_all_reaction_rates(double *states, double *rates, double *ydot)
Definition: rxd.cpp:1760
NRN_EXPORT void set_num_threads(const int n)
Definition: rxd.cpp:1209
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:1104
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:1079
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:1752
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:1317
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:1013
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:1336
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:1433
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:1142
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:633
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:743
void solve_reaction(ICSReactions *react, double *states, double *bval, double *cvode_states, double *cvode_b)
Definition: rxd.cpp:1524
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:855
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:1257
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:130
void _fadvance(void)
Definition: rxd.cpp:1268
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:775
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