NEURON
kssingle.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <OS/list.h>
5 #include "nrnoc2iv.h"
6 #include "kschan.h"
7 #include "kssingle.h"
8 
9 // needed for DiscreteEvent aspect
10 #include "netcvode.h"
11 #include "cvodeobj.h"
12 
13 //----------------
14 // KSSingleTrans, KSSingleState is only apparently redundant with KSTrans
15 // and KSState. i.e. they are identical only if KSChan has only a single
16 // gating complex with a power of 1. If there are G gating complexes with
17 // powers pg and numbers of states sg (where g ranges from 0 to G-1) then
18 // the number of distinct states for the gate g is the binomial factor
19 // nsg = (sg + pg - 1, pg). i.e. (sg + pg - 1)! / pg! / (sg - 1)!
20 // and the total number of distinct states is the product of the nsg.
21 // consider m^3 * h, and suppose m is open and M is closed then there
22 // are three ways to go from mmmh to Mmmh and one way to go to mmmH. i.e
23 // MMMH <-> mMMH (3am, bm) MMMH <-> MMMh (ah, bh) MMMh <-> mMMh (3am, bm)
24 // mMMH <-> mmMH (2am, 2bm) mMMH <-> mMMh (ah, bh) mMMh <-> mmMh (2am, 2bm)
25 // mmMH <-> mmmH (am, 3bm) mmMH <-> mmMh (ah, bh) mmMh <-> mmmh (am, 3bm)
26 // there are 8 states and 18 transitions.
27 //
28 // Following a single particle as it moves from state to state is
29 // the simplest case. It suffices to keep track of the state index
30 // and, following ...
31 // use two random numbers. One random number is transformed into
32 // a negative exponential distribution
33 // and defines the time interval to the next transition. The rate is the
34 // sum of all the possible transition rates. The other is uniformly
35 // distibuted between 0 and 1 and is used to determine which transition
36 // branch was taken according to the
37 //
38 // For the case of N particles distributed among the states we use
39 // an integer array in which an element is the integer population of the
40 // corresponding state. Too bad we lose the efficiency of the implicit handling
41 // of 0 population states but I don't see how it can be helped.
42 // Again we use two random numbers. The transition rate of some event, i.e.
43 // some particle moves to another state, is the sum over all states of the
44 // state population times the sum of all the possible transition rates for
45 // that state. And, again, the second random number defines the branch taken.
46 // Note that this is still an exact stochastic simulation in that only
47 // one thing happens at a time.
48 //
49 // It remains to discuss how things are affected by changing rates.
50 // We make the approximation that rates are constant within a dt. In
51 // consequence we only need to compute the rate information once per dt
52 // (at least for the N particle case) no matter how many transitions have
53 // to be followed til the last one is beyond the t0+dt point. The question
54 // is what to do with that last interval which remains unused, ie. time
55 // has not reached the "transition time"
56 // Also we'd like to be efficient when the average interval is much greater
57 // than dt.
58 // The simplest implementation is to just discard the random numbers and
59 // assert that the time is t0 <- t0+dt and compute from that point
60 // at the next solve step. The alternative is to hold the last random
61 // pair and recalculate the interval (assuming the rate has changed)
62 // to obtain (from the last actual transition time) a new transition time.
63 // and see if it is now less than the new t0 + dt. Note that there is
64 // some extra error here because of the assumption of constant rate over the
65 // last entire random interval and not just the last dt.
66 // Note that we do not have to worry about this (or at least worry less)
67 // in voltage clamp context as well as in contexts in which intervals are
68 // seldom > dt. However no analysis has been done and I can't demonstrate
69 // that the statistical error of following a single channel
70 // during an action potential is not substantial. We compromise and
71 // test the voltage. If it has changed then we reset the transition time
72 // to t0 and repick random numbers. If it has not changed significantly,
73 // then we keep the last random interval and use that when t0+dt passes the
74 // up to then unexecuted transition.
75 //----------------
76 // The above broad design envisioned a parallel structure involving
77 // a single kinetic scheme isomorphic to the user level spec which
78 // may have multiple gates raised to powers. The initial implementation
79 // for the KSSingle constructor, however, for simplicity assumed that the
80 // user level spec was only one ks gate complex with power 1. In any case
81 // the question of state read out and conductance calculation arises
82 // and in either case it seems to make most sense that the standard
83 // state array in the prop->param array be used. With the current
84 // limited implementation, KSSingleNodeData.statelist_ is conceptually
85 // identical to the prop->param states. In the envisioned full implementation
86 // the least violent possibility is to keep the user level spec as much
87 // as possible but to expand the prop->param states to correspond
88 // to single channel requirements. Another possibility which does a
89 // lot more violence to the user spec but makes the current implementation
90 // almost complete is to replace the user spec with a single channel
91 // adequate ks scheme. Merely stating it is sufficient to reject it.
92 // A third possiblity is to not use the prop->param states at all since
93 // their user level spec does not correspond to the ks single channel
94 // states. But this makes GUI interface not re-usable (e.g. plotting
95 // a state) and the question of determining states values at the user
96 // level remains. The last possibility is to not allow single channel
97 // mode unless there is only one ks gate complex raised to a single power
98 // and coelesce the redundant structures.
99 // So it seems that the best extension so far is to
100 // keep the user level spec in KSChan but when in single channel mode,
101 // define the prop->param state array to correspond to single channel
102 // states. Since we are going that far we might as well go the whole
103 // way and also introduce a new Nsingle PARAMETER and make the
104 // dparam[2]._pvoid == KSSingleNodeData existent only in single channel
105 // mode. Note that since KSChan.nstate_ <= KSSingle.nstate_, initialization
106 // can fill the KSChan interpreted prop->param states with the fractions,
107 // and then, per normal, the prop->param states interpreted at KSSingle
108 // states get filled with the right integers. We'll also need a naming
109 // convention for KSSingle states since that is how they get accessed at
110 // the hoc level. We need names for the hh open and closed state
111 // if the hh state name is "a". We need names generated for powers.
112 // Products are easy, just concatenate the names. For powers of hh states we
113 // use the hh state name followed by the number that are open. eg an m^3
114 // gate gets the names m0, m1, m2, m3. Thus m^3*h has the names
115 // m0h0 m1h0 m2h0 m3h0 m0h1 m1h1 m2h1 m3h1 (m3h1 is the only open state)
116 // Kind of makes mh[4][2] attractive.
117 // The combinatorics for ks gates raised to a power
118 // is more complex and it is not clear that that is so useful. I think
119 // it makes sense to ignore that and use array indices only for hh gates
120 // raised to a power. Thus a 3 state gate multiplied by an h hh gate would
121 // have the names c0h[2], c1h[2], oh[2]
122 // The nice aspect of this is that the current implementation can be
123 // easily transformed since the transformation of prop->param states can
124 // be deferred to later. i.e there seems to be a nice incremental path
125 // to a full implementation without removing at one step what was added
126 // at the previous step. The first step is to replace the not always used
127 // int* KSSingleNodeData.statelist_ with an always used
128 // double* KSSingleNodeData.statepop_ which points into the prop->param.
129 //----------------
130 
131 extern int cvode_active_;
133 
134 double KSSingle::vres_;
135 unsigned int KSSingle::idum_;
136 
138  // implemenation assumes one ks gate complex with power 1
139  int i;
140  sndindex_ = 2;
141  nstate_ = c->nstate_;
143  ntrans_ = 2 * c->ntrans_;
145  rval_ = new double[Math::max(ntrans_, nstate_)];
146  uses_ligands_ = false;
147  for (i = 0; i < c->ntrans_; ++i) {
148  {
149  KSSingleTrans& tf = transitions_[2 * i];
150  tf.kst_ = c->trans_ + i;
151  if (tf.kst_->type_ >= 2) {
152  uses_ligands_ = true;
153  }
154  tf.f_ = true;
155  tf.fac_ = 1.;
156  tf.src_ = tf.kst_->src_;
157  tf.target_ = tf.kst_->target_;
158  }
159  {
160  KSSingleTrans& tf = transitions_[2 * i + 1];
161  tf.kst_ = c->trans_ + i;
162  tf.f_ = false;
163  tf.fac_ = 1.;
164  tf.src_ = tf.kst_->target_;
165  tf.target_ = tf.kst_->src_;
166  }
167  }
168  // The transition list for each source state is required for
169  // N=1 single channels.
170  // count first
171  for (i = 0; i < ntrans_; ++i) {
173  }
174  // allocate and reset count
175  for (i = 0; i < nstate_; ++i) {
176  KSSingleState& ss = states_[i];
177  ss.transitions_ = new int[ss.ntrans_];
178  ss.ntrans_ = 0;
179  }
180  // link
181  for (i = 0; i < ntrans_; ++i) {
183  ss.transitions_[ss.ntrans_] = i;
184  ++ss.ntrans_;
185  }
186 }
187 
189  delete[] states_;
190  delete[] transitions_;
191  delete[] rval_;
192 }
193 
195  ntrans_ = 0;
196  transitions_ = NULL;
197 }
199  if (transitions_) {
200  delete[] transitions_;
201  }
202 }
203 
207  if (kst_->type_ < 2) {
208  return rate(NODEV(pnt->node));
209  } else {
210  return rate(pnt->prop->dparam);
211  }
212 }
213 
215  statepop_ = NULL;
216  nsingle_ = 1;
217 }
218 
220 
221 void KSSingleNodeData::deliver(double tt, NetCvode* nc, NrnThread* nt) {
223  Cvode* cv = (Cvode*) ((*ppnt_)->nvi_);
224  if (cv) {
225  nc->retreat(tt, cv);
226  cv->set_init_flag();
227  }
228  assert(nt->_t == tt);
229  vlast_ = NODEV((*ppnt_)->node);
230  if (nsingle_ == 1) {
231  kss_->do1trans(this);
232  } else {
233  kss_->doNtrans(this);
234  }
235  qi_ = nc->event(t1_, this, nt);
236 }
237 
238 void KSSingleNodeData::pr(const char* s, double tt, NetCvode* nc) {
239  Printf("%s %s %.15g\n", s, hoc_object_name((*ppnt_)->ob), tt);
240 }
241 
242 void KSSingle::state(Node* nd, double* p, Datum* pd, NrnThread* nt) {
243  // integrate from t-dt to t
244  int i;
245  double v = NODEV(nd);
246  KSSingleNodeData* snd = (KSSingleNodeData*) pd[sndindex_]._pvoid;
247  // if truly single channel, as opposed to N single channels
248  // then follow the one populated state. Otherwise do the
249  // general case
250  if (snd->nsingle_ == 1) {
251  one(v, snd, nt);
252  } else {
253  multi(v, snd, nt);
254  }
255 }
256 
257 void KSSingle::cv_update(Node* nd, double* p, Datum* pd, NrnThread* nt) {
258  // if v changed then need to move the outstanding
259  // single channel event time to a recalculated time
260  int i;
261  double v = NODEV(nd);
262  KSSingleNodeData* snd = (KSSingleNodeData*) pd[sndindex_]._pvoid;
263  if (uses_ligands_ || !vsame(v, snd->vlast_)) {
264  assert(nt->_t < snd->t1_);
265  snd->vlast_ = v;
266  snd->t0_ = nt->_t;
267  if (snd->nsingle_ == 1) {
268  next1trans(snd);
269  } else {
270  nextNtrans(snd);
271  }
272  net_cvode_instance->move_event(snd->qi_, snd->t1_, nt);
274  }
275 }
276 
278  int i;
279  --n;
280  double x = unifrand(rval_[n]);
281  for (i = 0; i < n; ++i) {
282  if (rval_[i] >= x) {
283  return i;
284  }
285  }
286  return n;
287 }
288 
289 void KSSingle::one(double v, KSSingleNodeData* snd, NrnThread* nt) {
290  if (uses_ligands_ || !vsame(v, snd->vlast_)) {
291  snd->vlast_ = v;
292  snd->t0_ = nt->_t - nt->_dt;
293  next1trans(snd);
294  }
295  while (snd->t1_ <= nt->_t) {
296  snd->vlast_ = v;
297  do1trans(snd);
298  }
299 }
300 
302  snd->t0_ = snd->t1_;
303  // printf("KSSingle::do1trans t1=%g old=%d ", snd->t1_, snd->filledstate_);
304  snd->statepop_[snd->filledstate_] = 0.;
306  snd->statepop_[snd->filledstate_] = 1.;
307  // printf("new=%d \n", snd->filledstate_);
308  next1trans(snd);
309 }
310 
312  int i;
313  KSSingleState* ss = states_ + snd->filledstate_;
314  double x = 0;
315  for (i = 0; i < ss->ntrans_; ++i) {
317  x += st->rate(*snd->ppnt_);
318  rval_[i] = x;
319  }
320  if (x > 1e-9) {
321  snd->t1_ = exprand() / x + snd->t0_;
322  snd->next_trans_ = ss->transitions_[rvalrand(ss->ntrans_)];
323  } else {
324  snd->t1_ = 1e9 + snd->t0_;
325  snd->next_trans_ = ss->transitions_[0];
326  }
327  // printf("KSSingle::next1trans v=%g t1_=%g %d (%d, %d)\n", snd->vlast_, snd->t1_,
328  // snd->next_trans_, transitions_[snd->next_trans_].src_,
329  // transitions_[snd->next_trans_].target_);
330 }
331 void KSSingle::multi(double v, KSSingleNodeData* snd, NrnThread* nt) {
332  if (uses_ligands_ || !vsame(v, snd->vlast_)) {
333  snd->vlast_ = v;
334  snd->t0_ = nt->_t - nt->_dt;
335  nextNtrans(snd);
336  }
337  while (snd->t1_ <= nt->_t) {
338  snd->vlast_ = v;
339  doNtrans(snd);
340  }
341 }
342 
344  snd->t0_ = snd->t1_;
346  assert(snd->statepop_[st->src_] >= 1.);
347  --snd->statepop_[st->src_];
348  ++snd->statepop_[st->target_];
349  // printf("KSSingle::doNtrans t1=%g %d with %g -> %d with %g\n", snd->t1_,
350  // st->src_, snd->statepop_[st->src_], st->target_, snd->statepop_[st->target_]);
351  nextNtrans(snd);
352 }
353 
355  int i;
356  double x = 0;
357  for (i = 0; i < ntrans_; ++i) {
358  KSSingleTrans* st = transitions_ + i;
359  x += snd->statepop_[st->src_] * st->rate(*snd->ppnt_);
360  rval_[i] = x;
361  }
362  if (x > 1e-9) {
363  snd->t1_ = exprand() / x + snd->t0_;
364  snd->next_trans_ = rvalrand(ntrans_);
365  } else {
366  snd->t1_ = 1e9 + snd->t0_;
367  snd->next_trans_ = 0;
368  }
369 }
370 
371 void KSSingle::alloc(Prop* p, int sindex) { // and discard old if not NULL
372  KSSingleNodeData* snd = (KSSingleNodeData*) (p->dparam[2]._pvoid);
373  if (snd) {
374  delete snd;
375  }
376  snd = new KSSingleNodeData();
377  snd->kss_ = this;
378  snd->ppnt_ = (Point_process**) (&p->dparam[1]._pvoid);
379  p->dparam[2]._pvoid = snd;
380  snd->statepop_ = p->param + sindex;
381 }
382 
383 void KSSingle::init(double v, double* s, KSSingleNodeData* snd, NrnThread* nt) {
384  // assuming 1-1 correspondence between KSChan and KSSingle states
385  // place steady state population intervals end to end
386  int i;
387  double x = 0;
388  snd->qi_ = NULL;
389  snd->t0_ = nt->_t;
390  snd->vlast_ = v;
391  for (i = 0; i < nstate_; ++i) {
392  x += s[i];
393  rval_[i] = x;
394  }
395  // initialization of complex kinetic schemes often not accurate to 9 decimal places
396  // assert(Math::equal(rval_[nstate_ - 1], 1., 1e-9));
397  for (i = 0; i < nstate_; ++i) {
398  snd->statepop_[i] = 0;
399  }
400  if (snd->nsingle_ == 1) {
401  snd->filledstate_ = rvalrand(nstate_);
402  ++snd->statepop_[snd->filledstate_];
403  next1trans(snd);
404  } else {
405  for (i = 0; i < snd->nsingle_; ++i) {
406  ++snd->statepop_[rvalrand(nstate_)];
407  }
408  nextNtrans(snd);
409  // for (i=0; i < nstate_; ++i) {
410  // printf(" state %d pop %g\n", i, snd->statepop_[i]);
411  //}
412  }
413  if (cvode_active_) {
414  snd->qi_ = net_cvode_instance->event(snd->t1_, snd, nrn_threads);
415  }
416 }
417 
420  if (snd) {
421  snd->nsingle_ = n;
422  }
423 }
426  if (snd) {
427  return snd->nsingle_;
428  } else {
429  return 1000000000;
430  }
431 }
Definition: cvodeobj.h:76
void set_init_flag()
Definition: cvodeobj.cpp:803
Definition: kschan.h:291
int nsingle(Point_process *)
Definition: kssingle.cpp:424
double unifrand(double range)
Definition: kssingle.h:61
double * rval_
Definition: kssingle.h:69
double exprand()
Definition: kssingle.h:58
int rvalrand(int)
Definition: kssingle.cpp:277
void alloc(Prop *, int sindex)
Definition: kssingle.cpp:371
void init(double v, double *s, KSSingleNodeData *snd, NrnThread *)
Definition: kssingle.cpp:383
void nextNtrans(KSSingleNodeData *)
Definition: kssingle.cpp:354
KSSingleTrans * transitions_
Definition: kssingle.h:67
virtual ~KSSingle()
Definition: kssingle.cpp:188
int nstate_
Definition: kssingle.h:66
void one(double, KSSingleNodeData *, NrnThread *)
Definition: kssingle.cpp:289
void next1trans(KSSingleNodeData *)
Definition: kssingle.cpp:311
int sndindex_
Definition: kssingle.h:66
void doNtrans(KSSingleNodeData *)
Definition: kssingle.cpp:343
static unsigned long singleevent_deliver_
Definition: kssingle.h:74
void state(Node *, double *, Datum *, NrnThread *)
Definition: kssingle.cpp:242
int ntrans_
Definition: kssingle.h:66
void cv_update(Node *, double *, Datum *, NrnThread *)
Definition: kssingle.cpp:257
void do1trans(KSSingleNodeData *)
Definition: kssingle.cpp:301
static unsigned int idum_
Definition: kssingle.h:72
bool vsame(double x, double y)
Definition: kssingle.h:55
static unsigned long singleevent_move_
Definition: kssingle.h:75
void multi(double, KSSingleNodeData *, NrnThread *)
Definition: kssingle.cpp:331
static double vres_
Definition: kssingle.h:71
KSSingle(KSChan *)
Definition: kssingle.cpp:137
KSSingleState * states_
Definition: kssingle.h:68
bool uses_ligands_
Definition: kssingle.h:70
double * statepop_
Definition: kssingle.h:27
virtual ~KSSingleNodeData()
Definition: kssingle.cpp:219
double vlast_
Definition: kssingle.h:29
virtual void pr(const char *, double t, NetCvode *)
Definition: kssingle.cpp:238
TQItem * qi_
Definition: kssingle.h:35
Point_process ** ppnt_
Definition: kssingle.h:33
virtual void deliver(double t, NetCvode *, NrnThread *)
Definition: kssingle.cpp:221
KSSingle * kss_
Definition: kssingle.h:34
virtual ~KSSingleState()
Definition: kssingle.cpp:198
int * transitions_
Definition: kssingle.h:101
double fac_
Definition: kssingle.h:93
virtual ~KSSingleTrans()
Definition: kssingle.cpp:205
KSTransition * kst_
Definition: kssingle.h:91
double rate(Point_process *)
Definition: kssingle.cpp:206
int src_
Definition: kschan.h:204
int target_
Definition: kschan.h:205
int type_
Definition: kschan.h:209
void retreat(double, Cvode *)
Definition: netcvode.cpp:3612
void move_event(TQItem *, double, NrnThread *)
Definition: netcvode.cpp:2379
TQItem * event(double tdeliver, DiscreteEvent *, NrnThread *)
Definition: netcvode.cpp:2712
#define c
char * hoc_object_name(Object *ob)
Definition: hoc_oop.cpp:72
#define assert(ex)
Definition: hocassrt.h:32
int cvode_active_
Definition: fadvance.cpp:163
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
#define max(a, b)
Definition: matrix.h:154
#define v
Definition: md1redef.h:4
#define i
Definition: md1redef.h:12
#define Printf
Definition: model.h:237
NrnThread * nrn_threads
Definition: multicore.cpp:47
int const size_t const size_t n
Definition: nrngsl.h:11
if(status)
size_t p
#define e
Definition: passive0.cpp:22
#define NODEV(n)
Definition: section.h:115
#define NULL
Definition: sptree.h:16
Definition: section.h:133
Represent main neuron object computed by single thread.
Definition: multicore.h:58
double _dt
Definition: multicore.h:60
double _t
Definition: multicore.h:59
Node * node
Definition: section.h:264
Prop * prop
Definition: section.h:265
Definition: section.h:214
Datum * dparam
Definition: section.h:220
Definition: hocdec.h:177
void * _pvoid
Definition: hocdec.h:187