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) { uses_ligands_ = true; }
152  tf.f_ = true;
153  tf.fac_ = 1.;
154  tf.src_ = tf.kst_->src_;
155  tf.target_ = tf.kst_->target_;
156  }{
157  KSSingleTrans& tf = transitions_[2*i + 1];
158  tf.kst_ = c->trans_ + i;
159  tf.f_ = false;
160  tf.fac_ = 1.;
161  tf.src_ = tf.kst_->target_;
162  tf.target_ = tf.kst_->src_;
163  }
164  }
165  // The transition list for each source state is required for
166  // N=1 single channels.
167  // count first
168  for (i=0; i < ntrans_; ++i) {
170  }
171  // allocate and reset count
172  for (i=0; i < nstate_; ++i) {
173  KSSingleState& ss = states_[i];
174  ss.transitions_ = new int[ss.ntrans_];
175  ss.ntrans_ = 0;
176  }
177  // link
178  for (i=0; i < ntrans_; ++i) {
180  ss.transitions_[ss.ntrans_] = i;
181  ++ss.ntrans_;
182  }
183 }
184 
186  delete [] states_;
187  delete [] transitions_;
188  delete [] rval_;
189 }
190 
192  ntrans_ = 0;
193  transitions_ = NULL;
194 }
196  if (transitions_) {
197  delete [] transitions_;
198  }
199 }
200 
202 }
204 }
206  if (kst_->type_ < 2) {
207  return rate(NODEV(pnt->node));
208  }else{
209  return rate(pnt->prop->dparam);
210  }
211 }
212 
214  statepop_ = NULL;
215  nsingle_ = 1;
216 }
217 
219 }
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_, snd->next_trans_,
328 //transitions_[snd->next_trans_].src_, transitions_[snd->next_trans_].target_);
329 }
330 void KSSingle::multi(double v, KSSingleNodeData* snd, NrnThread* nt) {
331  if (uses_ligands_ || !vsame(v, snd->vlast_)) {
332  snd->vlast_ = v;
333  snd->t0_ = nt->_t - nt->_dt;
334  nextNtrans(snd);
335  }
336  while (snd->t1_ <= nt->_t) {
337  snd->vlast_ = v;
338  doNtrans(snd);
339  }
340 }
341 
343  snd->t0_ = snd->t1_;
345  assert(snd->statepop_[st->src_] >= 1.);
346  --snd->statepop_[st->src_];
347  ++snd->statepop_[st->target_];
348 //printf("KSSingle::doNtrans t1=%g %d with %g -> %d with %g\n", snd->t1_,
349 //st->src_, snd->statepop_[st->src_], st->target_, snd->statepop_[st->target_]);
350  nextNtrans(snd);
351 }
352 
354  int i;
355  double x = 0;
356  for (i = 0; i < ntrans_; ++i) {
357  KSSingleTrans* st = transitions_ + i;
358  x += snd->statepop_[st->src_] * st->rate(*snd->ppnt_);
359  rval_[i] = x;
360  }
361  if (x > 1e-9) {
362  snd->t1_ = exprand()/x + snd->t0_;
363  snd->next_trans_ = rvalrand(ntrans_);
364  }else{
365  snd->t1_ = 1e9 + snd->t0_;
366  snd->next_trans_ = 0;
367  }
368 }
369 
370 void KSSingle::alloc(Prop* p, int sindex) { // and discard old if not NULL
372  if (snd) {
373  delete snd;
374  }
375  snd = new KSSingleNodeData();
376  snd->kss_ = this;
377  snd->ppnt_ = (Point_process**)(&p->dparam[1]._pvoid);
378  p->dparam[2]._pvoid = snd;
379  snd->statepop_ = p->param + sindex;
380 }
381 
382 void KSSingle::init(double v, double* s, KSSingleNodeData* snd, NrnThread* nt) {
383  // assuming 1-1 correspondence between KSChan and KSSingle states
384  // place steady state population intervals end to end
385  int i;
386  double x = 0;
387  snd->qi_ = NULL;
388  snd->t0_ = nt->_t;
389  snd->vlast_ = v;
390  for (i=0; i < nstate_; ++i) {
391  x += s[i];
392  rval_[i] = x;
393  }
394 // initialization of complex kinetic schemes often not accurate to 9 decimal places
395 // assert(Math::equal(rval_[nstate_ - 1], 1., 1e-9));
396  for (i=0; i < nstate_; ++i) {
397  snd->statepop_[i] = 0;
398  }
399  if (snd->nsingle_ == 1) {
400  snd->filledstate_ = rvalrand(nstate_);
401  ++snd->statepop_[snd->filledstate_];
402  next1trans(snd);
403  }else{
404  for (i=0; i < snd->nsingle_; ++i) {
405  ++snd->statepop_[rvalrand(nstate_)];
406  }
407  nextNtrans(snd);
408 //for (i=0; i < nstate_; ++i) {
409 // printf(" state %d pop %g\n", i, snd->statepop_[i]);
410 //}
411  }
412  if (cvode_active_) {
413  snd->qi_ = net_cvode_instance->event(snd->t1_, snd, nrn_threads);
414  }
415 }
416 
419  if (snd) {
420  snd->nsingle_ = n;
421  }
422 }
425  if (snd) {
426  return snd->nsingle_;
427  }else{
428  return 1000000000;
429  }
430 }
431 
TQItem * qi_
Definition: kssingle.h:35
KSSingleState * states_
Definition: kssingle.h:62
void nextNtrans(KSSingleNodeData *)
Definition: kssingle.cpp:353
#define Printf
Definition: model.h:252
double max(double a, double b)
Definition: geometry3d.cpp:22
double * param
Definition: section.h:218
double unifrand(double range)
Definition: kssingle.h:57
#define assert(ex)
Definition: hocassrt.h:26
KSTransition * trans_
Definition: kschan.h:353
int nsingle(Point_process *)
Definition: kssingle.cpp:423
TQItem * event(double tdeliver, DiscreteEvent *, NrnThread *)
Definition: netcvode.cpp:2623
void * _pvoid
Definition: hocdec.h:186
int src_
Definition: kschan.h:157
static unsigned int idum_
Definition: kssingle.h:66
double vlast_
Definition: kssingle.h:29
KSSingle * kss_
Definition: kssingle.h:34
#define NODEV(n)
Definition: section.h:114
int type_
Definition: kschan.h:162
if(status)
int * transitions_
Definition: kssingle.h:91
bool uses_ligands_
Definition: kssingle.h:64
virtual void pr(const char *, double t, NetCvode *)
Definition: kssingle.cpp:238
char * hoc_object_name(Object *ob)
Definition: hoc_oop.cpp:84
size_t p
Represent main neuron object computed by single thread.
Definition: multicore.h:58
void retreat(double, Cvode *)
Definition: netcvode.cpp:3543
Point_process ** ppnt_
Definition: kssingle.h:33
int nstate_
Definition: kssingle.h:60
nd
Definition: treeset.cpp:893
KSTransition * kst_
Definition: kssingle.h:81
#define v
Definition: md1redef.h:4
static unsigned long singleevent_move_
Definition: kssingle.h:69
virtual ~KSSingleTrans()
Definition: kssingle.cpp:203
static double vres_
Definition: kssingle.h:65
#define e
Definition: passive0.cpp:24
Node * node
Definition: section.h:263
void one(double, KSSingleNodeData *, NrnThread *)
Definition: kssingle.cpp:289
virtual ~KSSingleState()
Definition: kssingle.cpp:195
double _dt
Definition: multicore.h:60
void doNtrans(KSSingleNodeData *)
Definition: kssingle.cpp:342
double rate(Point_process *)
Definition: kssingle.cpp:205
int cvode_active_
Definition: fadvance.cpp:158
int const size_t const size_t n
Definition: nrngsl.h:12
_CONST char * s
Definition: system.cpp:74
NrnThread * nrn_threads
Definition: multicore.cpp:45
void alloc(Prop *, int sindex)
Definition: kssingle.cpp:370
static unsigned long singleevent_deliver_
Definition: kssingle.h:68
void multi(double, KSSingleNodeData *, NrnThread *)
Definition: kssingle.cpp:330
Prop * prop
Definition: section.h:264
void cv_update(Node *, double *, Datum *, NrnThread *)
Definition: kssingle.cpp:257
void do1trans(KSSingleNodeData *)
Definition: kssingle.cpp:301
void next1trans(KSSingleNodeData *)
Definition: kssingle.cpp:311
virtual ~KSSingleNodeData()
Definition: kssingle.cpp:218
Definition: section.h:213
double fac_
Definition: kssingle.h:83
void init(double v, double *s, KSSingleNodeData *snd, NrnThread *)
Definition: kssingle.cpp:382
Datum * dparam
Definition: section.h:219
KSSingle(KSChan *)
Definition: kssingle.cpp:137
int ntrans_
Definition: kssingle.h:60
double _t
Definition: multicore.h:59
int ntrans_
Definition: kschan.h:342
int nstate_
Definition: kschan.h:348
KSSingleTrans * transitions_
Definition: kssingle.h:61
Definition: cvodeobj.h:75
#define i
Definition: md1redef.h:12
void set_init_flag()
Definition: cvodeobj.cpp:790
double exprand()
Definition: kssingle.h:56
#define c
void move_event(TQItem *, double, NrnThread *)
Definition: netcvode.cpp:2312
double * statepop_
Definition: kssingle.h:27
int rvalrand(int)
Definition: kssingle.cpp:277
virtual ~KSSingle()
Definition: kssingle.cpp:185
Definition: kschan.h:239
Definition: section.h:132
int target_
Definition: kschan.h:158
virtual void deliver(double t, NetCvode *, NrnThread *)
Definition: kssingle.cpp:221
Definition: hocdec.h:176
double * rval_
Definition: kssingle.h:63
int sndindex_
Definition: kssingle.h:60
return NULL
Definition: cabcode.cpp:461
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
bool vsame(double x, double y)
Definition: kssingle.h:55
void state(Node *, double *, Datum *, NrnThread *)
Definition: kssingle.cpp:242