NEURON
bbsavestate.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 /*
4 
5 The goal is to be able to save state to a file and restore state when the
6 save and restore contexts have different numbers of processors, different
7 distribution of gids, and different splitting. It needs to work efficiently
8 in the context of many GByte file sizes and many thousands of processors.
9 
10 The major assumption is that Point_process order is the same within a Node.
11 It is difficult to assert the correctness of this assumption unless the user
12 defines a global identifier for each Point_process and modifies the
13 BBSaveState implementation to explicitly check that parameter with
14 f->i(ppgid, 1).
15 This has been relaxed a bit to allow point processes to be marked IGNORE
16 which will skip them on save/restore. However, it still holds that
17 the order of the non-ignored point processes must be the same within a Node.
18 When a property is encountered the type is checked. However there is no
19 way to determine if two point processes of the same type are restored in
20 the saved order in a Node. Also note that when a point process is ignored,
21 that also implies that the NetCons for which it is a target are also
22 ignored. Finally, note that a non-ignored point process that is saved
23 cannot be marked IGNORE on restore. Similarly, if a point process is
24 marked IGNORE on save it cannot be non-ignored on restore.
25 
26 The user can mark a point process IGNORE by calling the method
27 bbss.ignore(point_process_object)
28 on all the point processes to be ignored.
29 The internal list of ignored point processes can be cleared by calling
30 bbss.ignore()
31 
32 Because a restore clears the event queue and because one cannot call
33 finitialize from hoc without vitiating the restore, Vector.play will
34 not work unless one calls BBSaveState.vector_play_init() after a
35 restore (similarly frecord() must be called for Vector.record to work.
36 Note that it is necessary that Vector.play use a tvec argument with
37 a first element greater than or equal to the restore time.
38 
39 Because of many unimplemented aspects to the general problem,
40 this implementation is more or less limited to BlueBrain cortical models.
41 There are also conceptual difficulties with the general problem since
42 necessary information is embodied in the Hoc programs.
43 
44 Some model restrictions:
45 1) "Real" cells must have gids.
46 2) Artificial cells can have gids. If not they must be directly connected
47  to just one synapse (e.g. NetStim -> NetCon -> synapse).
48 3) There is only one spike output port per cell and that is associated
49  with a base gid.
50 4) NetCon.event in Hoc can be used only with NetCon's with a None source.
51 
52 Note: On reading, the only things that need to exist in the file are the gids
53 and sections that are needed and the others are ignored. So subsets of a written
54 model can read the same file. However, again, all the Point_process objects
55 have to exist for each section that were written by the file (unless they
56 they are eplicitly marked IGNORE).
57 
58 We keep together, all the information associated with a section which includes
59 Point_processes and the NetCons, and SelfEvents targeting each Point_process.
60 Sometimes a NetStim stimulus is used to stimulate the PointProcess.
61 For those cases, when the NetCon has no srcid, the source is also associated
62 with the Point_process.
63 
64 Originally, NetCon with an ARTIFICIAL_CELL source were ignored and did
65 not appear in the DEList of the point process. This allowed one to have
66 different numbers of NetStim with NetCon connected locally before a save
67 and restore. Note that outstanding events from these NetCon on the
68 queue were not saved. PreSyn with state are associated with the cell
69 (gid)that contains them. We required PreSyn to be associated with gids.
70 Although that convention had some nice properties (albeit, it involved some
71 extra programming complexity) it has been abandoned because in practice,
72 stimuli such as InhPoissonStim are complex and it is desirable that they
73 continue past a save/restore.
74 
75 We share more or less the same code for counting, writing, and reading by
76 using subclasses of BBSS_IO.
77 
78 The implementation is based on what was learned from a prototype
79 bbsavestate python module which turned out to be too slow. In particular,
80 the association of Point_Process and its list of NetCons and queue SelfEvents
81 needs to be kept in a hash map. NetCon order in the list is assumed to
82 be consistent for writing and reading but srcgids are checked for consistency
83 on reading.(note that is not a guarantee of correctness since it is possible
84 for two NetCons to have the same source and the same target. However the
85 Blue Brain project models at present have a one-to-one correspondence between
86 NetCon and synapse Point_process so even the idea of a list is overdoing it.)
87 
88 We assume only NetCon and SelfEvent queue events need to be saved. Others
89 that are under user control have to be managed by the user since reading
90 will clear the queue (except for an initialized NetParEvent). In particular
91 all cvode.event need to be re-issued. One efficiency we realize is to avoid
92 saving the queue with regard to NetCon events and instead save the
93 gid, spiketime information and re-issue the NetCon.send appropriately if not
94 already delivered.
95 
96 On further thought, the last sentence above needs some clarification.
97 Because of the variation in NetCon.delay, in general some spikes
98 for a given PreSyn have already been delivered and some spikes are
99 still on the queue. Nevertheless, it is only necessary to issue
100 the PreSyn.send with its proper initiation time on the source machine
101 and then do a spike exchange. On the target machine only the
102 NetCon spikes with delivery time >= t need to be re-issued from the
103 PreSyn.send. For this reason all the global NetCon events are
104 resolved into a set of source PreSyn events and associated with the
105 cell portion containing the spike generation site. This is very similar
106 to the problem solved by nrn_fake_fire when helping implement PatternStim
107 and to extend that for use here we only needed an extra mode
108 (fake_out = 2) with a one line change.
109 On the other hand, all the SelfEvents are associated with the cell
110 portion containing the target synapse.
111 
112 So, associated with a Section are the point processes and PreSyn, and
113 associated with point processes are NetCon and SelfEvent
114 
115 To allow Random state to be saved for POINT_PROCESS, if a POINT_PROCESS
116 declares FUNCTION bbsavestate, that function is called when the
117 POINT_PROCESS instance is saved and restored.
118 Also now allow Random state to be saved for a SUFFIX (density mechanism)
119 if it declares FUNCTION bbsavestate. Same interface.
120 The API of FUNCTION bbsavestate has been modified to allow saving/restoring
121 several values. FUNCTION bbsavestate takes two pointers to double arrays,
122 xdir and xval.
123 The first double array, xdir, has length 1 and xdir[0] is -1.0, 0.0, or 1.0
124 If xdir[0] == -1.0, then replace the xdir[0] with the proper number of elements
125 of xval and return 0.0. If xdir[0] == 0.0, then save double values into
126 the xval array (which will be sized correctly from a previous call with
127 xdir[0] == -1.0). If xdir[0] == 1.0, then the saved double values are in
128 xval and should be restored to their original values.
129 The number of elements saved/restored has to be apriori known by the instance
130 since the size of the xval that was saved is not sent to the instance on
131 restore.
132 
133 For example
134  FUNCTION bbsavestate() {
135  bbsavestate = 0
136  VERBATIM
137  double *xdir, *xval; // , *hoc_pgetarg();
138  xdir = hoc_pgetarg(1);
139  if (*xdir == -1.) { *xdir = 2; return 0.0; }
140  xval = hoc_pgetarg(2);
141  if (*xdir == 0.) { xval[0] = 20.; xval[1] = 21.;}
142  if (*xdir == 1) { printf("%d %d\n", xval[0]==20.0, xval[1] == 21.0); }
143  ENDVERBATIM
144  }
145 
146 When spike compression is used, there are only 256 time slots available
147 for spike exchange time within an integration interval. However, during
148 restore, we are sending the gid spike initiation time which can be
149 earlier than the current integration interval (and therefore may have
150 a negative slot index.
151 To avoid this problem, we must temporarily turn off both
152 spike and gid compression so that PreSyn::send steers to
153 nrn2ncs_outputevent(output_index_, tt) instead of nrn_outputevent(localgid,tt)
154 and so nrn_spike_exchange does a non-compressed exchange.
155 We do not need to worry about bin queueing since it is the delivery time that
156 is enqueueed and that is always in the future.
157 
158 When bin queueing is used, a mechanism is needed to avoid the assertion
159 error in BinQ::enqueue (see nrncvode/sptbinq.cpp) when the enqueued event
160 has a delivery time earlier than the binq current time. One possibility
161 is to turn off bin queueing and force all events on the standard queue
162 to be on binq boundaries. Another possibility is for bbsavestate to
163 take over bin queueing in this situation. The first possibility is
164 simpler but entails a slight loss of performance when dealing with the
165 normally binned events on the standard queue when simulation takes up again.
166 Let's try trapping the assertion error in BinQ::enqueue and executing a
167 callback to bbss_early when needed.
168 */
169 
170 #include "bbsavestate.h"
171 #include "classreg.h"
172 #include "ndatclas.h"
173 #include "nrnoc2iv.h"
174 #include "ocfile.h"
175 #include <cmath>
176 #include <nrnmpiuse.h>
177 #include <stdio.h>
178 #include <stdlib.h>
179 #include <string>
180 #include <sys/stat.h>
181 #include <unordered_map>
182 #include <unordered_set>
183 
184 #include "netcon.h"
185 #include "nrniv_mf.h"
186 #include "tqueue.h"
187 #include "vrecitem.h"
188 
189 // on mingw, OUT became defined
190 #undef IN
191 #undef OUT
192 #undef CNT
193 
194 extern bool nrn_use_bin_queue_;
195 extern void (*nrn_binq_enqueue_error_handler)(double, TQItem*);
196 static void bbss_early(double td, TQItem* tq);
197 
198 typedef void (*ReceiveFunc)(Point_process*, double*, double);
199 
200 #include "membfunc.h"
201 extern int section_count;
202 extern "C" void nrn_shape_update();
203 extern Section* nrn_section_exists(char* name, int index, Object* cell);
204 extern Section** secorder;
205 extern ReceiveFunc* pnt_receive;
208 extern "C" void clear_event_queue();
211 extern PlayRecList* net_cvode_instance_prl();
212 extern void nrn_netcon_event(NetCon*, double);
213 extern double t;
214 typedef void (*PFIO)(int, Object*);
215 extern void nrn_gidout_iter(PFIO);
216 extern short* nrn_is_artificial_;
217 extern "C" {
218 extern void net_send(void**, double*, Point_process*, double, double);
219 extern void nrn_fake_fire(int gid, double firetime, int fake_out);
220 } // extern "C"
221 extern Object* nrn_gid2obj(int gid);
222 extern PreSyn* nrn_gid2presyn(int gid);
223 extern int nrn_gid_exists(int gid);
224 
225 #if NRNMPI
226 extern void nrn_spike_exchange(NrnThread*);
227 extern void nrnmpi_barrier();
228 extern void nrnmpi_int_alltoallv(int*, int*, int*, int*, int*, int*);
229 extern void nrnmpi_dbl_alltoallv(double*, int*, int*, double*, int*, int*);
230 extern int nrnmpi_int_allmax(int);
231 extern void nrnmpi_int_allgather(int* s, int* r, int n);
232 extern void nrnmpi_int_allgatherv(int* s, int* r, int* n, int* dspl);
233 extern void nrnmpi_dbl_allgatherv(double* s, double* r, int* n, int* dspl);
234 #else
236 static void nrnmpi_barrier() {}
237 static void nrnmpi_int_alltoallv(int* s, int* scnt, int* sdispl, int* r, int* rcnt, int* rdispl) {
238  for (int i = 0; i < scnt[0]; ++i) {
239  r[i] = s[i];
240  }
241 }
242 static void nrnmpi_dbl_alltoallv(double* s,
243  int* scnt,
244  int* sdispl,
245  double* r,
246  int* rcnt,
247  int* rdispl) {
248  for (int i = 0; i < scnt[0]; ++i) {
249  r[i] = s[i];
250  }
251 }
252 static int nrnmpi_int_allmax(int x) {
253  return x;
254 }
255 static void nrnmpi_int_allgather(int* s, int* r, int n) {
256  for (int i = 0; i < n; ++i) {
257  r[i] = s[i];
258  }
259 }
260 static void nrnmpi_int_allgatherv(int* s, int* r, int* n, int* dspl) {
261  for (int i = 0; i < n[0]; ++i) {
262  r[i] = s[i];
263  }
264 }
265 static void nrnmpi_dbl_allgatherv(double* s, double* r, int* n, int* dspl) {
266  for (int i = 0; i < n[0]; ++i) {
267  r[i] = s[i];
268  }
269 }
270 #endif // NRNMPI
271 
272 #if BGPDMA
273 extern bool use_bgpdma_;
274 #endif
275 
276 extern "C" Point_process* ob2pntproc(Object*);
277 extern void nrn_play_init();
279 
280 // turn off compression to avoid problems with presyn deliver earlier than
281 // restore time.
282 #if NRNMPI
283 extern bool nrn_use_compress_;
284 extern bool nrn_use_localgid_;
285 #endif
286 static bool use_spikecompress_;
287 static bool use_gidcompress_;
288 
289 static int callback_mode;
290 static void tqcallback(const TQItem* tq, int i);
291 typedef std::vector<TQItem*> TQItemList;
294 
295 #define QUEUECHECK 1
296 #if QUEUECHECK
297 static void bbss_queuecheck();
298 #endif
299 
300 // API
301 // see save_test_bin and restore_test_bin for an example of
302 // the use of this following interface. Note in particular the
303 // use in restore_test_bin of a prior clear_event_queue() in order
304 // to allow bbss_buffer_counts to pass an assert during the restore
305 // process.
306 
307 extern "C" void* bbss_buffer_counts(int* len, int** gids, int** sizes, int* global_size);
308 // First call to return the information needed to make the other
309 // calls. Returns a pointer used by the other methods.
310 // Caller is reponsible for freeing (using free() and not delete [])
311 // the returned gids and sizes arrays
312 // when finished. The sizes array and global_size are needed for the
313 // caller to construct proper buffer sizes to pass to
314 // bbss_save_global and bbss_save for filling in. The size of these
315 // arrays is returned in *len.
316 // They are not needed for restoring
317 // (since the caller is passing already filled in buffers that are read
318 // by bbss_restore_global and bbss_restore
319 // The gids returned are base gids. It is the callers responsibility
320 // to somehow concatenate buffers with the same gid (from different hosts)
321 // either after save or before restore and preserve the piece count
322 // of the number of concatenated buffers for each base gid.
323 // Global_size will only be non_zero for host 0.
324 
325 extern "C" void bbss_save_global(void* bbss, char* buffer, int sz);
326 // call only on host 0 with a buffer of size equal to the
327 // global_size returned from the bbss_buffer_counts call on host 0
328 // sz is the size of the buffer (for error checking only, buffer+sz is
329 // out of bounds)
330 
331 extern "C" void bbss_restore_global(void* bbss, char* buffer, int sz);
332 // call on all hosts with the buffer contents returned from the call
333 // to bbss_save_global
334 // This must be called prior to any calls to bbss_restore
335 // sz is the size of the buffer (error checking only)
336 // This also does some other important restore initializations.
337 
338 extern "C" void bbss_save(void* bbss, int gid, char* buffer, int sz);
339 // Call this for each item of the gids from bbss_buffer_counts along with
340 // a buffer of size from the corresponding sizes array. The buffer will
341 // be filled in with savestate information. The gid may be the same on
342 // different hosts, in which case it is the callers responsibility to
343 // concatentate buffers at some point to allow calling of bbss_restore
344 // sz is the size of the buffer (error checking only)
345 
346 extern "C" void bbss_restore(void* bbss, int gid, int npiece, char* buffer, int sz);
347 // Call this for each item of the gids from bbss_buffer_counts, the
348 // number of buffers that were concatenated for the gid, and the
349 // concatenated buffer (the concatenated buffer does NOT contain npiece
350 // as the first value in the char* buffer pointer)
351 // sz is the size of the buffer (error checking only)
352 
353 extern "C" void bbss_save_done(void* bbss);
354 // At the end of the save process, call this to cleanup.
355 // when this call returns, bbss will be invalid.
356 
357 extern "C" void bbss_restore_done(void* bbss);
358 // At the end of the restore process, call this to do
359 // some extra setting up and cleanup.
360 // when this call returns, bbss will be invalid.
361 
362 // 0 no debug, 1 print to stdout, 2 read/write to IO file
363 #define DEBUG 0
364 static int debug = DEBUG;
365 static char dbuf[1024];
366 #if DEBUG == 1
367 #define PDEBUG printf("%s\n", dbuf)
368 #else
369 #define PDEBUG f->s(dbuf, 1)
370 #endif
371 
373 
375 
376 static int usebin_; // 1 if using Buffer, 0 if using TxtFile
377 
378 class BBSS_Cnt: public BBSS_IO {
379  public:
380  BBSS_Cnt();
381  virtual ~BBSS_Cnt();
382  virtual void i(int& j, int chk = 0);
383  virtual void d(int n, double& p);
384  virtual void d(int n, double* p);
385  virtual void s(char* cp, int chk = 0);
386  virtual Type type();
387  int bytecnt();
388  int ni, nd, ns, nl;
389 
390  private:
391  int bytecntbin();
392  int bytecntasc();
393 };
395  ni = nd = ns = nl = 0;
396 }
398 void BBSS_Cnt::i(int& j, int chk) {
399  ++ni;
400  ++nl;
401 }
402 void BBSS_Cnt::d(int n, double& p) {
403  nd += n;
404  ++nl;
405 }
406 void BBSS_Cnt::d(int n, double* p) {
407  nd += n;
408  ++nl;
409 }
410 void BBSS_Cnt::s(char* cp, int chk) {
411  ns += strlen(cp) + 1;
412 }
414  return BBSS_IO::CNT;
415 }
417  return usebin_ ? bytecntbin() : bytecntasc();
418 }
420  return ni * sizeof(int) + nd * sizeof(double) + ns;
421 }
423  return ni * 12 + nd * 23 + ns + nl;
424 }
425 
426 class BBSS_TxtFileOut: public BBSS_IO {
427  public:
428  BBSS_TxtFileOut(const char*);
429  virtual ~BBSS_TxtFileOut();
430  virtual void i(int& j, int chk = 0);
431  virtual void d(int n, double& p);
432  virtual void d(int n, double* p);
433  virtual void s(char* cp, int chk = 0);
434  virtual Type type();
435  FILE* f;
436 };
438  f = fopen(fname, "w");
439  assert(f);
440 }
442  fclose(f);
443 }
444 void BBSS_TxtFileOut::i(int& j, int chk) {
445  fprintf(f, "%12d\n", j);
446 }
447 void BBSS_TxtFileOut::d(int n, double& p) {
448  d(n, &p);
449 }
450 void BBSS_TxtFileOut::d(int n, double* p) {
451  for (int i = 0; i < n; ++i) {
452  fprintf(f, " %22.15g", p[i]);
453  }
454  fprintf(f, "\n");
455 }
456 void BBSS_TxtFileOut::s(char* cp, int chk) {
457  fprintf(f, "%s\n", cp);
458 }
460  return BBSS_IO::OUT;
461 }
462 
463 class BBSS_TxtFileIn: public BBSS_IO {
464  public:
465  BBSS_TxtFileIn(const char*);
466  virtual ~BBSS_TxtFileIn();
467  virtual void i(int& j, int chk = 0);
468  virtual void d(int n, double& p) {
469  d(n, &p);
470  }
471  virtual void d(int n, double* p);
472  virtual void s(char* cp, int chk = 0);
473  virtual Type type() {
474  return BBSS_IO::IN;
475  }
476  virtual void skip(int);
477  FILE* f;
478 };
480  f = fopen(fname, "r");
481  if (!f) {
482  hoc_execerr_ext("Could not open %s", fname);
483  }
484 }
486  fclose(f);
487 }
488 void BBSS_TxtFileIn::i(int& j, int chk) {
489  int k;
490  int rval = fscanf(f, "%d\n", &k);
491  assert(rval == 1);
492  if (chk) {
493  assert(j == k);
494  }
495  j = k;
496 }
497 void BBSS_TxtFileIn::d(int n, double* p) {
498  for (int i = 0; i < n; ++i) {
499  nrn_assert(fscanf(f, " %lf", p + i) == 1);
500  }
501  nrn_assert(fscanf(f, "\n") == 0);
502 }
503 void BBSS_TxtFileIn::s(char* cp, int chk) {
504  char buf[100];
505  nrn_assert(fscanf(f, "%[^\n]\n", buf) == 1);
506  if (chk) {
507  assert(strcmp(buf, cp) == 0);
508  }
509  strcpy(cp, buf);
510 }
512  for (int i = 0; i < n; ++i) {
513  fgetc(f);
514  }
515 }
516 
517 class BBSS_BufferOut: public BBSS_IO {
518  public:
519  BBSS_BufferOut(char* buffer, int size);
520  virtual ~BBSS_BufferOut();
521  virtual void i(int& j, int chk = 0);
522  virtual void d(int n, double& p);
523  virtual void d(int n, double* p);
524  virtual void s(char* cp, int chk = 0);
525  virtual Type type();
526  virtual void a(int);
527  virtual void cpy(int size, char* cp);
528  int sz;
529  char* b;
530  char* p;
531 };
532 BBSS_BufferOut::BBSS_BufferOut(char* buffer, int size) {
533  b = buffer;
534  p = b;
535  sz = size;
536 }
538 void BBSS_BufferOut::i(int& j, int chk) {
539  cpy(sizeof(int), (char*) (&j));
540 }
541 void BBSS_BufferOut::d(int n, double& d) {
542  cpy(sizeof(double), (char*) (&d));
543 }
544 void BBSS_BufferOut::d(int n, double* d) {
545  cpy(n * sizeof(double), (char*) d);
546 }
547 void BBSS_BufferOut::s(char* cp, int chk) {
548  cpy(strlen(cp) + 1, cp);
549 }
550 void BBSS_BufferOut::a(int i) {
551  assert((p - b) + i <= sz);
552 }
554  return BBSS_IO::OUT;
555 }
556 void BBSS_BufferOut::cpy(int ns, char* cp) {
557  a(ns);
558  for (int ii = 0; ii < ns; ++ii) {
559  p[ii] = cp[ii];
560  }
561  p += ns;
562 }
564  public:
565  BBSS_BufferIn(char* buffer, int size);
566  virtual ~BBSS_BufferIn();
567  virtual void i(int& j, int chk = 0);
568  virtual void s(char* cp, int chk = 0);
569  virtual void skip(int n) {
570  p += n;
571  }
572  virtual Type type();
573  virtual void cpy(int size, char* cp);
574 };
575 BBSS_BufferIn::BBSS_BufferIn(char* buffer, int size)
576  : BBSS_BufferOut(buffer, size) {}
578 void BBSS_BufferIn::i(int& j, int chk) {
579  int k;
580  cpy(sizeof(int), (char*) (&k));
581  if (chk) {
582  assert(j == k);
583  }
584  j = k;
585 }
586 void BBSS_BufferIn::s(char* cp, int chk) {
587  if (chk) {
588  assert(strcmp(p, cp) == 0);
589  }
590  cpy(strlen(p) + 1, cp);
591 }
593  return BBSS_IO::IN;
594 }
595 void BBSS_BufferIn::cpy(int ns, char* cp) {
596  a(ns);
597  for (int ii = 0; ii < ns; ++ii) {
598  cp[ii] = p[ii];
599  }
600  p += ns;
601 }
602 
603 static void* cons(Object*) {
604  BBSaveState* ss = new BBSaveState();
605  return (void*) ss;
606 }
607 
608 static void destruct(void* v) {
609  BBSaveState* ss = (BBSaveState*) v;
610  delete ss;
611 }
612 
613 static double save(void* v) {
614  usebin_ = 0;
615  BBSaveState* ss = (BBSaveState*) v;
616  BBSS_IO* io = new BBSS_TxtFileOut(gargstr(1));
617  io->d(1, nrn_threads->_t); // save time
618  ss->apply(io);
619  delete io;
620  return 1.;
621 }
622 
623 static void bbss_restore_begin() {
625 
626  // turn off compression. Will turn back on in bbss_restore_done.
627 #if NRNMPI
628  use_spikecompress_ = nrn_use_compress_;
629  use_gidcompress_ = nrn_use_localgid_;
630  nrn_use_compress_ = false;
631  nrn_use_localgid_ = false;
632 #endif
633 
634  if (nrn_use_bin_queue_) {
635  // Start the BinQ with the same time it had at save time.
637  tq->shift_bin(nrn_threads->_t - 0.5 * nrn_threads->_dt);
639  }
640 }
641 
642 static double restore(void* v) {
643  usebin_ = 0;
644  BBSaveState* ss = (BBSaveState*) v;
645  BBSS_IO* io = new BBSS_TxtFileIn(gargstr(1));
646  io->d(1, t);
647  nrn_threads->_t = t; // single thread assumption here
648 
650  ss->apply(io);
651  delete io;
653  return 1.;
654 }
655 
656 #include <ivocvect.h>
657 static double save_request(void* v) {
658  int *gids, *sizes;
659  Vect* gidvec = vector_arg(1);
660  Vect* sizevec = vector_arg(2);
661  BBSaveState* ss = (BBSaveState*) v;
662  int len = ss->counts(&gids, &sizes);
663  gidvec->resize(len);
664  sizevec->resize(len);
665  for (int i = 0; i < len; ++i) {
666  gidvec->elem(i) = double(gids[i]);
667  sizevec->elem(i) = double(sizes[i]);
668  }
669  if (len) {
670  free(gids);
671  free(sizes);
672  }
673  return double(len);
674 }
675 
676 static double save_gid(void* v) {
677  printf("save_gid not implemented\n");
678  return 0.;
679 }
680 
681 static double restore_gid(void* v) {
682  printf("restore_gid not implemented\n");
683  return 0.;
684 }
685 
686 static void pycell_name2sec_maps_clear();
687 
688 static double save_test(void* v) {
689  int *gids, *sizes;
690  BBSaveState* ss = (BBSaveState*) v;
691  usebin_ = 0;
692  if (nrnmpi_myid == 0) { // save global time
693 #if defined(MINGW)
694  mkdir("bbss_out");
695 #else
696  mkdir("bbss_out", 0770);
697 #endif
698  BBSS_IO* io = new BBSS_TxtFileOut("bbss_out/tmp");
699  io->d(1, nrn_threads->_t);
700  delete io;
701  }
702  nrnmpi_barrier();
703 
704  int len = ss->counts(&gids, &sizes);
705  for (int i = 0; i < len; ++i) {
706  char fn[200];
707  sprintf(fn, "bbss_out/tmp.%d.%d", gids[i], nrnmpi_myid);
708  BBSS_IO* io = new BBSS_TxtFileOut(fn);
709  ss->f = io;
710  ss->gidobj(gids[i]);
711  delete io;
712  }
713  if (len) {
714  free(gids);
715  free(sizes);
716  }
717  return 0.;
718 }
719 
720 static double save_test_bin(void* v) { // only for whole cells
721  int len, *gids, *sizes, global_size;
722  char* buf;
723  char fname[100];
724  FILE* f;
725  usebin_ = 1;
726  void* ref = bbss_buffer_counts(&len, &gids, &sizes, &global_size);
727  if (nrnmpi_myid == 0) { // save global time
728  buf = new char[global_size];
729  bbss_save_global(ref, buf, global_size);
730  sprintf(fname, "binbufout/global.%d", global_size);
731  nrn_assert(f = fopen(fname, "w"));
732  fwrite(buf, sizeof(char), global_size, f);
733  fclose(f);
734  delete[] buf;
735 
736  sprintf(fname, "binbufout/global.size");
737  nrn_assert(f = fopen(fname, "w"));
738  fprintf(f, "%d\n", global_size);
739  fclose(f);
740  }
741  for (int i = 0; i < len; ++i) {
742  buf = new char[sizes[i]];
743  bbss_save(ref, gids[i], buf, sizes[i]);
744  sprintf(fname, "binbufout/%d.%d", gids[i], sizes[i]);
745  nrn_assert(f = fopen(fname, "w"));
746  fwrite(buf, sizeof(char), sizes[i], f);
747  fclose(f);
748  delete[] buf;
749 
750  sprintf(fname, "binbufout/%d.size", gids[i]);
751  nrn_assert(f = fopen(fname, "w"));
752  fprintf(f, "%d\n", sizes[i]);
753  fclose(f);
754  }
755  if (len) {
756  free(gids);
757  free(sizes);
758  }
759  bbss_save_done(ref);
760  return 0.;
761 }
762 
763 typedef std::unordered_map<Point_process*, int> PointProcessMap;
764 static std::unique_ptr<PointProcessMap> pp_ignore_map;
765 
766 static double ppignore(void* v) {
767  if (ifarg(1)) {
769  if (!pp_ignore_map) {
770  pp_ignore_map.reset(new PointProcessMap());
771  pp_ignore_map->reserve(100);
772  }
773  (*pp_ignore_map)[pp] = 0; // naive set instead of map
774  } else if (pp_ignore_map) { // clear
775  pp_ignore_map.reset();
776  }
777  return 0.;
778 }
779 
780 static int ignored(Prop* p) {
781  Point_process* pp = (Point_process*) p->dparam[1]._pvoid;
782  if (pp_ignore_map) {
783  if (pp_ignore_map->count(pp) > 0) {
784  return 1;
785  }
786  }
787  return 0;
788 }
789 
790 extern "C" void* bbss_buffer_counts(int* len, int** gids, int** sizes, int* global_size) {
791  usebin_ = 1;
792  BBSaveState* ss = new BBSaveState();
793  *global_size = 0;
794  if (nrnmpi_myid == 0) { // save global time
795  BBSS_Cnt* io = new BBSS_Cnt();
796  io->d(1, nrn_threads->_t);
797  *global_size = io->bytecnt();
798  delete io;
799  }
800  *len = ss->counts(gids, sizes);
801  return ss;
802 }
803 extern "C" void bbss_save_global(void* bbss, char* buffer,
804  int sz) { // call only on host 0
805  usebin_ = 1;
806  BBSS_IO* io = new BBSS_BufferOut(buffer, sz);
807  io->d(1, nrn_threads->_t);
808  delete io;
809 }
810 extern "C" void bbss_restore_global(void* bbss, char* buffer,
811  int sz) { // call on all hosts
812  usebin_ = 1;
813  BBSS_IO* io = new BBSS_BufferIn(buffer, sz);
814  io->d(1, nrn_threads->_t);
815  t = nrn_threads->_t;
816  delete io;
818 }
819 extern "C" void bbss_save(void* bbss, int gid, char* buffer, int sz) {
820  usebin_ = 1;
821  BBSaveState* ss = (BBSaveState*) bbss;
822  BBSS_IO* io = new BBSS_BufferOut(buffer, sz);
823  ss->f = io;
824  ss->gidobj(gid);
825  delete io;
826 }
827 extern "C" void bbss_restore(void* bbss, int gid, int ngroup, char* buffer, int sz) {
828  usebin_ = 1;
829  BBSaveState* ss = (BBSaveState*) bbss;
830  BBSS_IO* io = new BBSS_BufferIn(buffer, sz);
831  ss->f = io;
832  for (int i = 0; i < ngroup; ++i) {
833  ss->gidobj(gid);
834  t = nrn_threads->_t;
835  }
836  delete io;
837 }
838 extern "C" void bbss_save_done(void* bbss) {
839  BBSaveState* ss = (BBSaveState*) bbss;
840  delete ss;
841 }
842 
843 static void bbss_remove_delivered() {
845 
846  // PreSyn and NetCon spikes are on the queue. To determine the spikes
847  // that have already been delivered the PreSyn items that have
848  // NetCon delivery times < t need to get fanned out to NetCon items
849  // on the queue before checking the times.
851  callback_mode = 2;
853  for (TQItem* qi: *tq_presyn_fanout) {
854  double td = qi->t_;
855  PreSyn* ps = (PreSyn*) qi->data_;
856  tq->remove(qi);
857  ps->fanout(td, net_cvode_instance, nrn_threads);
858  }
859  delete tq_presyn_fanout;
860 
861  // now everything on the queue which is subject to removal is a NetCon
862  tq_removal_list = new TQItemList();
863  callback_mode = 3;
865  for (TQItem* qi: *tq_removal_list) {
866  int type = ((DiscreteEvent*) qi->data_)->type();
867  if (type != NetConType) {
868  printf("%d type=%d\n", nrnmpi_myid, type);
869  }
870  assert(type == NetConType);
871  tq->remove(qi);
872  }
873  delete tq_removal_list;
874 }
875 
876 extern "C" void bbss_restore_done(void* bbss) {
877  if (bbss) {
878  BBSaveState* ss = (BBSaveState*) bbss;
879  delete ss;
880  }
881  // We did not save the NetParEvent. In fact, during
882  // saving, the minimum delay might be different than
883  // what is needed with this distribution and nhost.
884  NetParEvent* npe = new NetParEvent();
885  npe->ithread_ = 0;
886  npe->savestate_restore(t, net_cvode_instance);
887  delete npe;
889 #if BGPDMA
890  // only necessary if multisend method is using two subintervals
891  if (use_bgpdma_) {
893  }
894 #endif
895  // The queue may now contain spikes which have already been
896  // delivered and so those must be removed if delivery time < t.
897  // Actually, the Presyn spikes may end up as NetCon spikes some
898  // of which may have been already delivered and some not (due to
899  // variations in NetCon.delay .
901 
902 #if NRNMPI
903  // turn spike compression back on
904  nrn_use_localgid_ = use_gidcompress_;
905  nrn_use_compress_ = use_spikecompress_;
906 #endif
907 
908  // compare the restored queue count for each presyn spike with the
909  // expected undelivered NetCon count what was read from the file
910  // for each PreSyn. This is optional due to the computational
911  // expense but is helpful to point toward a diagnosis of errors.
912 #if QUEUECHECK
913  bbss_queuecheck();
914 #endif
916 }
917 
918 static double restore_test(void* v) {
919  usebin_ = 0;
920  int *gids, *sizes;
921  BBSaveState* ss = (BBSaveState*) v;
922  // restore global time
923  BBSS_IO* io = new BBSS_TxtFileIn("in/tmp");
924  io->d(1, t);
925  nrn_threads->_t = t;
926  delete io;
927 
929  int len = ss->counts(&gids, &sizes);
930  for (int i = 0; i < len; ++i) {
931  char fn[200];
932  sprintf(fn, "in/tmp.%d", gids[i]);
933  BBSS_IO* io = new BBSS_TxtFileIn(fn);
934  ss->f = io;
935  int ngroup;
936  ss->f->i(ngroup);
937  for (int j = 0; j < ngroup; ++j) {
938  ss->gidobj(gids[i]);
939  }
940  delete io;
941  }
942  if (len) {
943  free(gids);
944  free(sizes);
945  }
947  return 0.;
948 }
949 
950 static double restore_test_bin(void* v) { // assumes whole cells
951  usebin_ = 1;
952  int len, *gids, *sizes, global_size, npiece, sz;
953  void* ref;
954  char* buf;
955  char fname[100];
956  FILE* f;
957 
958  sprintf(fname, "binbufin/global.size");
959  nrn_assert(f = fopen(fname, "r"));
960  nrn_assert(fscanf(f, "%d\n", &sz) == 1);
961  fclose(f);
962  global_size = sz;
963  buf = new char[sz];
964 
965  sprintf(fname, "binbufin/global.%d", global_size);
966  f = fopen(fname, "r");
967  if (!f) {
968  printf("%d fail open for read %s\n", nrnmpi_myid, fname);
969  }
970  assert(f);
971  nrn_assert(fread(buf, sizeof(char), global_size, f) == global_size);
972  fclose(f);
973  bbss_restore_global(NULL, buf, global_size);
974  delete[] buf;
975 
976  ref = bbss_buffer_counts(&len, &gids, &sizes, &global_size);
977 
978  for (int i = 0; i < len; ++i) {
979  npiece = 1;
980 
981  sprintf(fname, "binbufin/%d.size", gids[i]);
982  nrn_assert(f = fopen(fname, "r"));
983  nrn_assert(fscanf(f, "%d\n", &sz) == 1);
984  fclose(f);
985  // if (sz != sizes[i]) {
986  // printf("%d note sz=%d size=%d\n", nrnmpi_myid, sz, sizes[i]);
987  //}
988 
989  buf = new char[sz];
990  sprintf(fname, "binbufin/%d.%d", gids[i], sz);
991  f = fopen(fname, "r");
992  if (!f) {
993  printf("%d fail open for read %s\n", nrnmpi_myid, fname);
994  }
995  assert(f);
996  nrn_assert(fread(buf, sizeof(char), sz, f) == sz);
997  fclose(f);
998  bbss_restore(ref, gids[i], npiece, buf, sz);
999  delete[] buf;
1000  }
1001 
1002  if (len) {
1003  free(gids);
1004  free(sizes);
1005  }
1006  bbss_restore_done(ref);
1007  return 0.;
1008 }
1009 
1010 static double vector_play_init(void* v) {
1011  nrn_play_init();
1012  return 0.;
1013 }
1014 
1015 static Member_func members[] = {
1016  // text test
1017  {"save", save},
1018  {"restore", restore},
1019  {"save_test", save_test},
1020  {"restore_test", restore_test},
1021  // binary test
1022  {"save_test_bin", save_test_bin},
1023  {"restore_test_bin", restore_test_bin},
1024  // binary save/restore interface to interpreter
1025  {"save_request", save_request},
1026  {"save_gid", save_gid},
1027  {"restore_gid", restore_gid},
1028  // indicate which point processes are to be ignored
1029  {"ignore", ppignore},
1030  // allow Vector.play to work
1031  {"vector_play_init", vector_play_init},
1032  {0, 0}};
1033 
1035  class2oc("BBSaveState", cons, destruct, members, NULL, NULL, NULL);
1036 }
1037 
1038 // from savstate.cpp
1040  int offset;
1041  int size;
1043 };
1045 static cTemplate* nct;
1046 static void ssi_def() {
1047  if (nct) {
1048  return;
1049  }
1050  Symbol* s = hoc_lookup("NetCon");
1051  nct = s->u.ctemplate;
1052  ssi = new StateStructInfo[n_memb_func];
1053  int sav = v_structure_change;
1054  for (int im = 0; im < n_memb_func; ++im) {
1055  ssi[im].offset = -1;
1056  ssi[im].size = 0;
1057  ssi[im].callback = 0;
1058  if (!memb_func[im].sym) {
1059  continue;
1060  }
1061  NrnProperty* np = new NrnProperty(memb_func[im].sym->name);
1062  // generally we only save STATE variables. However for
1063  // models containing a NET_RECEIVE block, we also need to
1064  // save everything except the parameters
1065  // because they often contain
1066  // logic and analytic state values. Unfortunately, it is often
1067  // the case that the ASSIGNED variables are not declared as
1068  // RANGE variables so to avoid missing state, save the whole
1069  // param array including PARAMETERs.
1070  if (pnt_receive[im]) {
1071  ssi[im].offset = 0;
1072  ssi[im].size = np->prop()->param_size;
1073  } else {
1074  int type = STATE;
1075  for (Symbol* sym = np->first_var(); np->more_var(); sym = np->next_var()) {
1076  if (np->var_type(sym) == type || np->var_type(sym) == STATE ||
1077  sym->subtype == _AMBIGUOUS) {
1078  if (ssi[im].offset < 0) {
1079  ssi[im].offset = np->prop_index(sym);
1080  }
1081  ssi[im].size += hoc_total_array_data(sym, 0);
1082  }
1083  }
1084  }
1085  if (memb_func[im].is_point) {
1086  // check for callback named bbsavestate
1087  cTemplate* ts = nrn_pnt_template_[im];
1088  ssi[im].callback = hoc_table_lookup("bbsavestate", ts->symtable);
1089  // if (ssi[im].callback) {
1090  // printf("callback %s.%s\n", ts->sym->name,
1091  // ssi[im].callback->name);
1092  //}
1093  } else {
1094  // check for callback named bbsavestate in a density mechanism
1095  char name[256];
1096  sprintf(name, "bbsavestate_%s", memb_func[im].sym->name);
1097  ssi[im].callback = hoc_table_lookup(name, hoc_built_in_symlist);
1098  // if (ssi[im].callback) {
1099  // printf("callback %s\n", ssi[im].callback->name);
1100  //}
1101  }
1102  delete np;
1103  }
1104  // Following set to 1 when NrnProperty constructor calls prop_alloc.
1105  // so set back to original value.
1106  v_structure_change = sav;
1107 }
1108 
1109 // if we know the Point_process, we can find the NetCon
1110 // BB project never has more than one NetCon connected to a Synapse.
1111 // But that may not hold in general so extend to List of NetCon using DEList.
1112 // and assume the list is same order on save/restore.
1113 typedef struct DEList {
1115  struct DEList* next;
1116 } DEList;
1117 typedef std::unordered_map<Point_process*, DEList*> PP2DE;
1118 static std::unique_ptr<PP2DE> pp2de;
1119 // NetCon.events
1120 typedef std::vector<double> DblList;
1121 typedef std::unordered_map<NetCon*, DblList*> NetCon2DblList;
1122 static std::unique_ptr<NetCon2DblList> nc2dblist;
1123 
1124 class SEWrap: public DiscreteEvent {
1125  public:
1126  SEWrap(const TQItem*, DEList*);
1127  ~SEWrap();
1128  int type() {
1129  return se->type();
1130  }
1132  double tt;
1133  int ncindex; // in the DEList or -1 if no NetCon for self event.
1134 };
1135 SEWrap::SEWrap(const TQItem* tq, DEList* dl) {
1136  tt = tq->t_;
1137  se = (SelfEvent*) tq->data_;
1138  if (se->weight_) {
1139  ncindex = 0;
1140  for (; dl && dl->de && dl->de->type() == NetConType; dl = dl->next, ++ncindex) {
1141  if (se->weight_ == ((NetCon*) dl->de)->weight_) {
1142  return;
1143  }
1144  }
1145  // There used to be an assert(0) here.
1146  // There is no associated NetCon (or the NetCon is being
1147  // ignored, perhaps because it is used to reactivate a
1148  // BinReportHelper after a bbsavestate restore).
1149  // In either case, we should also ignore the
1150  // SelfEvent. So send back a special ncindex sentinal value.
1151 
1152  ncindex = -2;
1153  } else {
1154  ncindex = -1;
1155  }
1156 }
1158 
1159 typedef std::vector<SEWrap*> SEWrapList;
1161 
1162 typedef std::unordered_map<int, int> Int2Int;
1163 static std::unique_ptr<Int2Int> base2spgid{new Int2Int()}; // base gids are the host independent key for a cell
1164  // which was multisplit
1165 
1166 typedef std::unordered_map<int, DblList*> Int2DblList;
1167 static std::unique_ptr<Int2DblList> src2send{new Int2DblList()};; // gid to presyn send time map
1168 static int src2send_cnt;
1169 // the DblList was needed in place of just a double because there might
1170 // be several spikes from a single PreSyn (interval between spikes less
1171 // than the maximum NetCon delay for that PreSyn).
1172 // Given a current bug regarding dropped spikes with bin queuing as well
1173 // as the question about the proper handling of a spike with delivery
1174 // time equal to the save state time, it is useful to provide a mechanism
1175 // that allows us to assert that the number of spikes on the queue
1176 // at a save (conceptually the number of NetCon fanned out from the
1177 // PreSyn that have not yet been delivered)
1178 // is identical to the number of spikes on the queue after
1179 // a restore. To help with this, we add a count of the "to be delivered"
1180 // NetCon spikes on the queue to each PreSyn item in the saved file.
1181 // For least effort, given the need to handle counts in the same
1182 // fashion as we handle the presyn send times in the DblList, we merely
1183 // represent (ts, cnt) pairs as adjacent items in the DblList.
1184 // For saving, the undelivered netcon count is always computed. After
1185 // restore this information can be checked against the actual
1186 // netcon count on the queue by defining QUEUECHECK to 1. Note that
1187 // computing the undelivered netcon count involves: 1) each machine
1188 // processing its queue's NetCon and PreSyn spikes using tqcallback
1189 // with callback_mode 1. 2) aggregating the PreSyn counts on some
1190 // machine using scatteritems(). 3) lastly sending the total count per
1191 // PreSyn to the machine that owns the PreSyn gid (see mk_presyn_info).
1192 // This 3-fold process is reused during restore to check the counts ane
1193 // we have factored out the relevant code so it can be used for both
1194 // save and restore (for the latter see bbss_queuecheck()).
1195 #if QUEUECHECK
1196 static std::unique_ptr<Int2DblList> queuecheck_gid2unc;
1197 #endif
1198 
1199 static double binq_time(double tt) {
1200  if (nrn_use_bin_queue_) {
1201  double dt = nrn_threads->_dt;
1202  // For a given event time, return a time which would put it in the
1203  // same bin on restore, that it came from on save.
1204  // Note that, before save, it was put on the queue via BinQ::enqueue
1205  // with int idt = (int)((td - tt_)/nt_dt + 1.e-10);
1206  // where tt_ is the early half step edge time of the current bin.
1207  double t1 = int(tt / dt + 0.5 + 1e-10) * dt;
1208  return t1;
1209  }
1210  return tt;
1211 }
1212 
1213 static void bbss_early(double td, TQItem* tq) {
1214  int type = ((DiscreteEvent*) tq->data_)->type();
1215  // If NetCon, discard. If PreSyn, fanout.
1216  if (type == NetConType) {
1217  return;
1218  } else if (type == PreSynType) {
1219  PreSyn* ps = (PreSyn*) tq->data_;
1220  ps->fanout(td, net_cvode_instance, nrn_threads);
1221  } else {
1222  assert(0);
1223  }
1224 }
1225 
1226 static void tqcallback(const TQItem* tq, int i) {
1227  int type = ((DiscreteEvent*) tq->data_)->type();
1228  switch (callback_mode) {
1229  case 0: { // the self events
1230  if (type == SelfEventType) {
1231  Point_process* pp = ((SelfEvent*) tq->data_)->target_;
1232  DEList* dl1 = NULL;
1233  SEWrap* sew = 0;
1234  const auto& dl1iter = pp2de->find(pp);
1235  if (dl1iter != pp2de->end()) {
1236  dl1 = dl1iter->second;
1237  sew = new SEWrap(tq, dl1);
1238  } else {
1239  sew = new SEWrap(tq, NULL);
1240  }
1241  if (sew->ncindex == -2) { // ignore the self event
1242  // printf("%d Ignoring a SelfEvent to %s\n", nrnmpi_myid,
1243  // memb_func[pp->prop->type].sym->name);
1244  delete sew;
1245  sew = 0;
1246  }
1247  if (sew) {
1248  sewrap_list->push_back(sew);
1249  DEList* dl = new DEList;
1250  dl->next = 0;
1251  dl->de = sew;
1252  if (dl1) {
1253  while (dl1->next) {
1254  dl1 = dl1->next;
1255  }
1256  dl1->next = dl;
1257  } else {
1258  (*pp2de)[pp] = dl;
1259  }
1260  }
1261  }
1262  } break;
1263 
1264  case 1: { // the NetCon (and PreSyn) events
1265  int srcid, i;
1266  double ts;
1267  PreSyn* ps;
1268  int cntinc; // number on queue to be delivered due to this event
1269  NetCon* nc = 0;
1270  if (type == NetConType) {
1271  nc = (NetCon*) tq->data_;
1272  ps = nc->src_;
1273  ts = tq->t_ - nc->delay_;
1274  cntinc = 1;
1275  } else if (type == PreSynType) {
1276  ps = (PreSyn*) tq->data_;
1277  ts = tq->t_ - ps->delay_;
1278  cntinc = ps->dil_.size();
1279  } else {
1280  return;
1281  }
1282  if (ps) {
1283  if (ps->gid_ >= 0) { // better not be from NetCon.event
1284  srcid = ps->gid_;
1285  DblList* dl;
1286  const auto& dliter = src2send->find(srcid);
1287  if (dliter != src2send->end()) {
1288  dl = dliter->second;
1289  // If delay is long and spiking
1290  // is fast there may be
1291  // another source spike when
1292  // its previous spike is still on
1293  // the queue. So we might have
1294  // to add another initiation time.
1295  // originally there was an assert error here under the assumption that
1296  // all spikes from a PreSyn were delivered before that PreSyn fired
1297  // again. The assumption did not hold for existing Blue Brain models.
1298  // Therefore we extend the algorithm to any number of spikes with
1299  // different initiation times from the same PreSyn. For sanity we
1300  // assume Presyns do not fire more than once every 0.1 ms.
1301  // Unfortunately this possibility makes mpi exchange much more
1302  // difficult as the number of doubles exchanged can be greater than
1303  // the number of src2send gids. Add another initiation time if needed
1304  double m = 1e9; // > .1 add
1305  int im = -1; // otherwise assert < 1e-12
1306  for (i = 0; i < dl->size(); i += 2) {
1307  double x = fabs((*dl)[i] - ts);
1308  if (x < m) {
1309  m = x;
1310  im = i;
1311  }
1312  }
1313  if (m > 0.1) {
1314  dl->push_back(ts);
1315  dl->push_back(cntinc);
1316  } else if (m > 1e-12) {
1317  assert(0);
1318  } else {
1319  (*dl)[im + 1] += cntinc;
1320  }
1321  } else {
1322  dl = new DblList();
1323  dl->push_back(ts);
1324  dl->push_back(cntinc);
1325  (*src2send)[srcid] = dl;
1326  ++src2send_cnt; // distinct PreSyn
1327  }
1328 
1329  } else {
1330  // osrc state should already have been associated
1331  // with the target Point_process and we should
1332  // now associate this event as well
1333  if (ps->osrc_) { // NetStim possibly
1334  // does not matter if NetCon.event
1335  // or if the event was sent from
1336  // the local stimulus. Can't be from
1337  // a PreSyn event since we demand
1338  // that there be only one NetCon
1339  // from this stimulus.
1340  assert(nc);
1341  DblList* db = 0;
1342  const auto& dbiter = nc2dblist->find(nc);
1343  if (dbiter == nc2dblist->end()) {
1344  db = new DblList();
1345  (*nc2dblist)[nc] = db;
1346  } else {
1347  db = dbiter->second;
1348  }
1349  db->push_back(tq->t_);
1350  } else { // assume from NetCon.event
1351  // ps should be unused_presyn
1352  // printf("From NetCon.event\n");
1353  assert(nc);
1354  DblList* db = 0;
1355  const auto& dbiter = nc2dblist->find(nc);
1356  if (dbiter == nc2dblist->end()) {
1357  db = new DblList();
1358  (*nc2dblist)[nc] = db;
1359  } else {
1360  db = dbiter->second;
1361  }
1362  db->push_back(tq->t_);
1363  }
1364  }
1365  }
1366  } break;
1367 
1368  case 2: {
1369  // some PreSyns may get fanned out into NetCon, only
1370  // some of which have already been delivered. If this is the
1371  // case, add the q item to the fanout list. Do not put in
1372  // list if all or none have been delivered.
1373  // Actually, for simplicity, just put all the PreSyn items in
1374  // the list for fanout if PreSyn::deliver time < t.
1375  if (type == PreSynType) {
1376  if (tq->t_ < t) {
1377  tq_presyn_fanout->push_back((TQItem*) tq);
1378  }
1379  }
1380  } break;
1381  case 3: {
1382  // ??? have the ones at t been delivered or not?
1383  if (type != NetConType) {
1384  break;
1385  }
1386  if (tq->t_ == t) {
1387  DiscreteEvent* de = (DiscreteEvent*) tq->data_;
1388  de->pr("Don't know if this event has already been delivered", t, net_cvode_instance);
1389  // assert(0);
1390  }
1391  double td = tq->t_;
1392  double tt = t;
1393  // td should be on bin queue boundary
1394  if (nrn_use_bin_queue_) {
1395  // not discarded if in the current bin
1397  }
1398  if (td <= tt) {
1399  //((DiscreteEvent*)tq->data_)->pr("tq_removal_list", td,
1400  // net_cvode_instance);
1401  tq_removal_list->push_back((TQItem*) tq);
1402  }
1403  } break;
1404  }
1405 }
1407  hoc_Item* q;
1408  assert(!pp2de); // one only or make it a field.
1409  int n = nct->count;
1410  pp2de.reset(new PP2DE);
1411  pp2de->reserve(n + 1);
1412  sewrap_list = new SEWrapList();
1413  ITERATE(q, nct->olist) {
1414  NetCon* nc = (NetCon*) OBJ(q)->u.this_pointer;
1415  // ignore NetCon with no PreSyn.
1416  // i.e we do not save or restore information about
1417  // NetCon.event. We do save NetCons with local NetStim source.
1418  // But that NetStim can only be the source for one NetCon.
1419  // (because its state info will be attached to the
1420  // target synapse)
1421  if (!nc->src_) {
1422  continue;
1423  }
1424  // has a gid or else only one connection
1425  assert(nc->src_->gid_ >= 0 || nc->src_->dil_.size() == 1);
1426  Point_process* pp = nc->target_;
1427  DEList* dl = new DEList;
1428  dl->de = nc;
1429  dl->next = 0;
1430  DEList* dl1;
1431  const auto& delistiter = pp2de->find(pp);
1432  // NetCons first then SelfEvents
1433  if (delistiter != pp2de->end()) {
1434  dl1 = delistiter->second;
1435  while (dl1->next) {
1436  dl1 = dl1->next;
1437  }
1438  dl1->next = dl;
1439  } else {
1440  (*pp2de)[pp] = dl;
1441  }
1442  }
1444  callback_mode = 0;
1446 }
1447 
1448 static std::unique_ptr<Int2DblList> presyn_queue;
1449 
1450 static void del_presyn_info() {
1451  if (presyn_queue) {
1452  for (const auto& dl: *presyn_queue) {
1453  delete dl.second;
1454  }
1455  presyn_queue.reset();
1456  }
1457  if (nc2dblist) {
1458  for (const auto& dl: *nc2dblist) {
1459  delete dl.second;
1460  }
1461  nc2dblist.reset();
1462  }
1463 }
1464 
1466  DEList* dl1;
1467  if (!pp2de) {
1468  return;
1469  }
1470  for (const auto& dlpair: *pp2de) {
1471  auto dl = dlpair.second;
1472  for (; dl; dl = dl1) {
1473  dl1 = dl->next;
1474  // need to delete SEWrap items but dl->de that
1475  // are not SEWrap may already be deleted.
1476  // Hence the extra sewrap_list and items are
1477  // deleted below.
1478  delete dl;
1479  }
1480  }
1481  pp2de.reset();
1482  if (sewrap_list) {
1483  for (SEWrap* sewrap: *sewrap_list) {
1484  delete sewrap;
1485  }
1486  delete sewrap_list;
1487  sewrap_list = NULL;
1488  }
1489  del_presyn_info();
1490 }
1491 
1494  if (!ssi) {
1495  ssi_def();
1496  }
1497 }
1499  if (pp2de) {
1500  del_pp2de();
1501  }
1503 }
1505  f = io;
1506  bbss = this;
1507  core();
1508 };
1509 
1511  if (debug) {
1512  sprintf(dbuf, "Enter core()");
1513  PDEBUG;
1514  }
1515  char buf[100];
1516  sprintf(buf, "//core");
1517  f->s(buf, 1);
1518  init();
1519  gids();
1520  finish();
1521  if (debug) {
1522  sprintf(dbuf, "Leave core()");
1523  PDEBUG;
1524  }
1525 }
1526 
1528  mk_base2spgid();
1529  mk_pp2de();
1530  mk_presyn_info();
1531 }
1532 
1534  del_pp2de();
1535  del_presyn_info();
1536  base2spgid.reset();
1537  if (f->type() == BBSS_IO::IN) {
1539  }
1540 }
1541 
1542 static void base2spgid_item(int spgid, Object* obj) {
1543  int base = spgid % 10000000;
1544  if (spgid == base || !base2spgid->count(base)) {
1545  (*base2spgid)[base] = spgid;
1546  }
1547  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1548  // must be Python Cell
1549  hoc_obj_unref(obj);
1550  }
1551 }
1552 
1554  base2spgid.reset(new Int2Int());
1555  base2spgid->reserve(1000);
1557 }
1558 
1559 // c++ blue brain write interface in two phases. First return to bb what is
1560 // needed for each gid and then get from bb a series of gid, buffer
1561 // pairs to write the buffer. The read interface only requires a single
1562 // phase where we receive from bb a series of gid, buffer pairs for
1563 // reading. However a final step is required to transfer PreSyn spikes by
1564 // calling BBSaveState:finish().
1565 
1566 // first phase for writing
1567 // the caller is responsible for free(*gids) and free(*cnts)
1568 // when finished with them.
1569 int BBSaveState::counts(int** gids, int** cnts) {
1570  f = new BBSS_Cnt();
1571  BBSS_Cnt* c = (BBSS_Cnt*) f;
1572  bbss = this;
1573  init();
1574  // how many
1575  int gidcnt = base2spgid->size();
1576  if (gidcnt) {
1577  // using malloc instead of new as we might need to
1578  // deallocate from c code (of mod files)
1579  *gids = (int*) malloc(gidcnt * sizeof(int));
1580  *cnts = (int*) malloc(gidcnt * sizeof(int));
1581 
1582  if (*gids == NULL || *cnts == NULL) {
1583  printf("Error : Memory allocation failure in BBSaveState\n");
1584 #if NRNMPI
1585  nrnmpi_abort(-1);
1586 #else
1587  abort();
1588 #endif
1589  }
1590  }
1591  gidcnt = 0;
1592  for (const auto& pair: *base2spgid) {
1593  auto base = pair.first;
1594  auto spgid = pair.second;
1595  (*gids)[gidcnt] = base;
1596  c->ni = c->nd = c->ns = c->nl = 0;
1597  Object* obj = nrn_gid2obj(spgid);
1598  gidobj(spgid, obj);
1599  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1600  hoc_obj_unref(obj);
1601  }
1602  (*cnts)[gidcnt] = c->bytecnt();
1603  ++gidcnt;
1604  }
1605  delete f;
1606  return gidcnt;
1607 }
1608 
1609 // note that a cell on a processor consists of any number of
1610 // pieces of a whole cell and each piece has its own spgid
1611 // (see nrn/share/lib/hoc/binfo.hoc) The piece with the output port
1612 // has spgid == basegid with a PreSyn.output_index_ = basegid
1613 // and the others have a PreSyn.output_index_ = -2
1614 // in all cases the PreSyn.gid_ = spgid. (see netpar.cpp BBS::cell())
1615 // Note that binfo.hoc provides base_gid(spgid) which is spgid%msgid
1616 // where msgid is typically 1e7 if the maximum basegid is less than that.
1617 // thishost_gid(basegid) returns the minimum spgid
1618 // of the pieces for the cell on thishost. It would be great if we
1619 // did not have to use the value of msgid. How could we safely derive it?
1620 // In general, different models invent different mappings between basegid
1621 // and msgid so ultimately (if not restricted to Blue Brain usage)
1622 // it is necessary to supply a user defined hoc (or python) callback
1623 // to compute base_gid(spgid). Then the writer and reader can create
1624 // a map of basegid to cell and use only basegid (never reading or writing
1625 // the spgid). If there is no basegid callback then we presume there
1626 // are no split cells.
1627 
1628 // a gid item for second phase of writing
1629 void BBSaveState::gid2buffer(int gid, char* buffer, int size) {
1630  if (f) {
1631  delete f;
1632  }
1633  f = new BBSS_BufferOut(buffer, size);
1634  Object* obj = nrn_gid2obj(gid);
1635  gidobj(gid, obj);
1636  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1637  hoc_obj_unref(obj);
1638  }
1639  delete f;
1640  f = 0;
1641 }
1642 
1643 // a gid item for first phase of reading
1644 void BBSaveState::buffer2gid(int gid, char* buffer, int size) {
1645  if (f) {
1646  delete f;
1647  }
1648  f = new BBSS_BufferIn(buffer, size);
1649  Object* obj = nrn_gid2obj(gid);
1650  gidobj(gid, obj);
1651  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1652  hoc_obj_unref(obj);
1653  }
1654  delete f;
1655  f = 0;
1656 }
1657 
1658 static void cb_gidobj(int gid, Object* obj) {
1659  bbss->gidobj(gid, obj);
1660  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1661  hoc_obj_unref(obj);
1662  }
1663 }
1664 
1666  if (debug) {
1667  sprintf(dbuf, "Enter gids()");
1668  PDEBUG;
1669  }
1671  if (debug) {
1672  sprintf(dbuf, "Leave gids()");
1673  PDEBUG;
1674  }
1675 }
1676 
1677 void BBSaveState::gidobj(int basegid) {
1678  int spgid;
1679  Object* obj;
1680  const auto& spgiditer = base2spgid->find(basegid);
1681  nrn_assert(spgiditer != base2spgid->end());
1682  spgid = spgiditer->second;
1683  obj = nrn_gid2obj(spgid);
1684  gidobj(spgid, obj);
1685  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1686  hoc_obj_unref(obj);
1687  }
1688 }
1689 
1690 void BBSaveState::gidobj(int gid, Object* obj) {
1691  char buf[256];
1692  int rgid = gid;
1693  if (debug) {
1694  sprintf(dbuf, "Enter gidobj(%d, %s)", gid, hoc_object_name(obj));
1695  PDEBUG;
1696  }
1697  sprintf(buf, "begin cell");
1698  f->s(buf, 1);
1699  f->i(rgid); // on reading, we promptly ignore rgid from now on, stick with gid
1700  int size = cellsize(obj);
1701  f->i(size);
1702  cell(obj);
1703  possible_presyn(gid);
1704  sprintf(buf, "end cell");
1705  f->s(buf, 1);
1706  if (debug) {
1707  sprintf(dbuf, "Leave gidobj(%d, %s)", gid, hoc_object_name(obj));
1708  PDEBUG;
1709  }
1710 }
1711 
1713  if (debug) {
1714  sprintf(dbuf, "Enter cellsize(%s)", hoc_object_name(c));
1715  PDEBUG;
1716  }
1717  int cnt = -1;
1718  if (f->type() == BBSS_IO::OUT) {
1719  BBSS_IO* sav = f;
1720  f = new BBSS_Cnt();
1721  cell(c);
1722  cnt = ((BBSS_Cnt*) f)->bytecnt();
1723  delete f;
1724  f = sav;
1725  }
1726  if (debug) {
1727  sprintf(dbuf, "Leave cellsize(%s)", hoc_object_name(c));
1728  PDEBUG;
1729  }
1730  return cnt;
1731 }
1732 
1733 // what is the section list for sections associated with a PythonObject.
1734 
1735 // Searching through the global section_list for each cell to create
1736 // a seclist for that cell has unacceptable quadratic
1737 // performance. So search once and create map from PythonCell to SecName2Sec
1738 // map. on first request. The SecName2Sec map associates the base section name'
1739 // to the Section* wrapped by a Python Section. Using a SecName2Sec map
1740 // rather than a seclist allows similar use for save and restore.
1741 
1742 typedef std::unordered_map<std::string, Section*> SecName2Sec;
1743 static std::unordered_map<void*, SecName2Sec> pycell_name2sec_maps;
1744 
1745 // clean up after a restore
1747  pycell_name2sec_maps.clear();
1748 }
1749 
1752  hoc_Item* qsec;
1753  ForAllSections(sec) // macro has the {
1754  if (sec->prop && sec->prop->dparam[PROP_PY_INDEX]._pvoid) { // PythonSection
1755  // Assume we can associate with a Python Cell
1756  // Sadly, cannot use nrn_sec2cell Object* as the key because it
1757  // is not unique and the map needs definite PyObject* keys.
1758  Object* ho = nrn_sec2cell(sec);
1759  if (ho) {
1760  void* pycell = nrn_opaque_obj2pyobj(ho);
1761  hoc_obj_unref(ho);
1762  if (pycell) {
1763  SecName2Sec& sn2s = pycell_name2sec_maps[pycell];
1764  std::string name = secname(sec);
1765  // basename is after the cell name component that ends in '.'.
1766  size_t last_dot = name.rfind(".");
1767  assert(last_dot != std::string::npos);
1768  assert(name.size() > (last_dot + 1));
1769  std::string basename = name.substr(last_dot + 1);
1770  if (sn2s.find(basename) != sn2s.end()) {
1771  hoc_execerr_ext("Python Section name, %s, is not unique in the Python cell",
1772  name.c_str());
1773  }
1774  sn2s[basename] = sec;
1775  continue;
1776  }
1777  }
1778  hoc_execerr_ext("Python Section, %s, not associated with Python Cell.", secname(sec));
1779  }
1780 }
1781 }
1782 
1784  if (pycell_name2sec_maps.empty()) {
1786  }
1787  void* pycell = nrn_opaque_obj2pyobj(c);
1788  auto search = pycell_name2sec_maps.find(pycell);
1789  assert(search != pycell_name2sec_maps.end());
1790  return search->second;
1791 }
1792 
1793 // Here is the major place where there is a symmetry break between writing
1794 // and reading. That is because of the possibility of splitting where
1795 // not only the pieces are distributed differently between saving and restoring
1796 // processors but the pieces themselves (as well as the piece gids)
1797 // are different. It is only the union of pieces that describes a complete cell.
1798 // Symmetry exists again at the section level
1799 // For writing the cell is defined as the existing set of pieces in the hoc
1800 // cell Object. Here in cell we write enough prefix info about the Section
1801 // to be able to determine if it exists before reading with section(sec);
1802 
1804  if (debug) {
1805  sprintf(dbuf, "Enter cell(%s)", hoc_object_name(c));
1806  PDEBUG;
1807  }
1808  char buf[256];
1809  sprintf(buf, "%s", hoc_object_name(c));
1810  f->s(buf);
1811  if (!is_point_process(c)) { // must be cell object
1812  if (f->type() != BBSS_IO::IN) { // writing, counting
1813  // from forall_section in cabcode.cpp
1814  // count, and iterate from first to last
1815  hoc_Item *qsec, *first, *last;
1816  int cnt = 0;
1817  Section* sec;
1818  qsec = c->secelm_;
1819  if (qsec) { // Write HOC Cell
1820  for (first = qsec; first->itemtype && hocSEC(first)->prop->dparam[6].obj == c;
1821  first = first->prev) {
1822  sec = hocSEC(first);
1823  if (sec->prop) {
1824  ++cnt;
1825  }
1826  }
1827  first = first->next;
1828  last = qsec->next;
1829  f->i(cnt);
1830  for (qsec = first; qsec != last; qsec = qsec->next) {
1831  Section* sec = hocSEC(qsec);
1832  if (sec->prop) {
1833  // the section exists
1834  sprintf(buf, "begin section");
1835  f->s(buf);
1836  section_exist_info(sec);
1837  section(sec);
1838  sprintf(buf, "end section");
1839  f->s(buf);
1840  }
1841  }
1842  } else { // Write Python Cell
1843  // secelm_ is NULL if c is a PythonObject. Need to get
1844  // the list of sections associated with c in some other way.
1845  SecName2Sec& n2s = pycell_name2sec_map(c);
1846  int i = (int) (n2s.size());
1847  f->i(i);
1848  for (auto& iter: n2s) {
1849  const std::string& name = iter.first;
1850  Section* sec = iter.second;
1851  assert(sec->prop); // all exist because n2s derived from global
1852  // section_list.
1853  sprintf(buf, "begin section");
1854  f->s(buf);
1855  strcpy(buf, name.c_str());
1856  f->s(buf);
1857  int indx = sec->prop->dparam[5].i;
1858  f->i(indx);
1859  int size = sectionsize(sec);
1860  f->i(size, 1);
1861  section(sec);
1862  sprintf(buf, "end section");
1863  f->s(buf);
1864  }
1865  }
1866  } else { // reading
1867  Section* sec;
1868  int i, cnt, indx, size;
1869 
1870  // Don't know a better idiom for following.
1871  SecName2Sec* n2s = NULL;
1872  if (!c->secelm_) {
1873  n2s = &pycell_name2sec_map(c);
1874  }
1875  // Assert that all section name are unique for a Python Cell
1876  std::unordered_set<std::string> snames;
1877 
1878  f->i(cnt);
1879  for (i = 0; i < cnt; ++i) {
1880  sprintf(buf, "begin section");
1881  f->s(buf, 1);
1882  f->s(buf); // the section name
1883  f->i(indx); // section array index
1884  f->i(size);
1885  if (c->secelm_) { // HOC cell
1886  sec = nrn_section_exists(buf, indx, c);
1887  } else {
1888  sec = NULL;
1889  if (snames.find(buf) != snames.end()) {
1890  hoc_execerr_ext("More than one section with name %s in cell %s",
1891  buf,
1892  hoc_object_name(c));
1893  }
1894  snames.emplace(buf);
1895  auto search = (*n2s).find(buf);
1896  if (search != (*n2s).end()) {
1897  sec = search->second;
1898  }
1899  }
1900  if (sec) {
1901  section(sec);
1902  } else { // skip size bytes
1903  f->skip(size);
1904  }
1905  sprintf(buf, "end section");
1906  f->s(buf, 1);
1907  }
1908  }
1909  } else { // ARTIFICIAL_CELL
1910  Point_process* pnt = ob2pntproc(c);
1911  mech(pnt->prop);
1912  }
1913  if (debug) {
1914  sprintf(dbuf, "Leave cell(%s)", hoc_object_name(c));
1915  PDEBUG;
1916  }
1917 }
1918 
1920  char buf[256];
1921  // not used for python sections
1923  Symbol* sym = sec->prop->dparam[0].sym;
1924  if (sym) {
1925  sprintf(buf, "%s", sym->name);
1926  f->s(buf);
1927  }
1928  int indx = sec->prop->dparam[5].i;
1929  f->i(indx);
1930  int size = sectionsize(sec);
1931  f->i(size, 1);
1932 }
1933 
1935  if (debug) {
1936  sprintf(dbuf, "Enter section(%s)", sec->prop->dparam[0].sym->name);
1937  PDEBUG;
1938  }
1939  seccontents(sec);
1940  if (debug) {
1941  sprintf(dbuf, "Leave section(%s)", sec->prop->dparam[0].sym->name);
1942  PDEBUG;
1943  }
1944 }
1945 
1947  if (debug == 1) {
1948  sprintf(dbuf, "Enter sectionsize(%s)", sec->prop->dparam[0].sym->name);
1949  PDEBUG;
1950  }
1951  // should be same for both IN and OUT
1952  int cnt = -1;
1953  if (f->type() != BBSS_IO::CNT) {
1954  BBSS_IO* sav = f;
1955  f = new BBSS_Cnt();
1956  seccontents(sec);
1957  cnt = ((BBSS_Cnt*) f)->bytecnt();
1958  delete f;
1959  f = sav;
1960  }
1961  if (debug == 1) {
1962  sprintf(dbuf, "Leave sectionsize(%s)", sec->prop->dparam[0].sym->name);
1963  PDEBUG;
1964  }
1965  return cnt;
1966 }
1967 
1969  if (debug) {
1970  sprintf(dbuf, "Enter seccontents(%s)", sec->prop->dparam[0].sym->name);
1971  PDEBUG;
1972  }
1973  int i, nseg;
1974  char buf[100];
1975  sprintf(buf, "//contents");
1976  f->s(buf);
1977  nseg = sec->nnode - 1;
1978  f->i(nseg, 1);
1979  for (i = 0; i < nseg; ++i) {
1980  node(sec->pnode[i]);
1981  }
1982  node01(sec, sec->parentnode);
1983  node01(sec, sec->pnode[nseg]);
1984  if (debug) {
1985  sprintf(dbuf, "Leave seccontents(%s)", sec->prop->dparam[0].sym->name);
1986  PDEBUG;
1987  }
1988 }
1989 
1990 // all Point_process and mechanisms -- except IGNORE point process instances
1992  if (debug) {
1993  sprintf(dbuf, "Enter node(nd)");
1994  PDEBUG;
1995  }
1996  int i;
1997  Prop* p;
1998  f->d(1, NODEV(nd));
1999  // count
2000  // On restore, new point processes may have been inserted in
2001  // the section and marked IGNORE. So we need to count only the
2002  // non-ignored.
2003  for (i = 0, p = nd->prop; p; p = p->next) {
2004  if (p->type > 3) {
2005  if (memb_func[p->type].is_point) {
2006  if (!ignored(p)) {
2007  ++i;
2008  }
2009  } else { // density mechanism
2010  ++i;
2011  }
2012  }
2013  }
2014  f->i(i, 1);
2015  for (p = nd->prop; p; p = p->next) {
2016  if (p->type > 3) {
2017  mech(p);
2018  }
2019  }
2020  if (debug) {
2021  sprintf(dbuf, "Leave node(nd)");
2022  PDEBUG;
2023  }
2024 }
2025 
2026 // only Point_process that belong to Section
2028  if (debug) {
2029  sprintf(dbuf, "Enter node01(sec, nd)");
2030  PDEBUG;
2031  }
2032  int i;
2033  Prop* p;
2034  // It is not clear why the zero area node voltages need to be saved.
2035  // Without them, we get correct simulations after a restore for
2036  // whole cells but not for split cells.
2037  f->d(1, NODEV(nd));
2038  // count
2039  for (i = 0, p = nd->prop; p; p = p->next) {
2040  if (memb_func[p->type].is_point) {
2041  Point_process* pp = (Point_process*) p->dparam[1]._pvoid;
2042  if (pp->sec == sec) {
2043  if (!ignored(p)) {
2044  ++i;
2045  }
2046  }
2047  }
2048  }
2049  f->i(i, 1);
2050  for (p = nd->prop; p; p = p->next) {
2051  if (memb_func[p->type].is_point) {
2052  Point_process* pp = (Point_process*) p->dparam[1]._pvoid;
2053  if (pp->sec == sec) {
2054  mech(p);
2055  }
2056  }
2057  }
2058  if (debug) {
2059  sprintf(dbuf, "Leave node01(sec, nd)");
2060  PDEBUG;
2061  }
2062 }
2063 
2065  if (debug) {
2066  sprintf(dbuf, "Enter mech(prop type %d)", p->type);
2067  PDEBUG;
2068  }
2069  int type = p->type;
2070  if (memb_func[type].is_point && ignored(p)) {
2071  return;
2072  }
2073  f->i(type, 1);
2074  char buf[100];
2075  sprintf(buf, "//%s", memb_func[type].sym->name);
2076  f->s(buf, 1);
2077  f->d(ssi[p->type].size, p->param + ssi[p->type].offset);
2078  Point_process* pp = 0;
2079  if (memb_func[p->type].is_point) {
2080  pp = (Point_process*) p->dparam[1]._pvoid;
2081  if (pnt_receive[p->type]) {
2082  // associated NetCon and queue SelfEvent
2083  // if the NetCon has a unique non-gid source (art cell)
2084  // that source is save/restored as well.
2085  netrecv_pp(pp);
2086  }
2087  }
2088  if (ssi[p->type].callback) { // model author dependent info
2089  // the POINT_PROCESS or SUFFIX has a bbsavestate function
2090  sprintf(buf, "callback");
2091  f->s(buf, 1);
2092  int narg = 2;
2093  double xdir = -1.0; // -1 size, 0 save, 1 restore
2094  double* xval = NULL; // returned for save, sent for restore.
2095 
2096  hoc_pushpx(&xdir);
2097  hoc_pushpx(xval);
2098  if (memb_func[p->type].is_point) {
2099  hoc_call_ob_proc(pp->ob, ssi[p->type].callback, narg);
2100  hoc_xpop();
2101  } else {
2102  nrn_call_mech_func(ssi[p->type].callback, narg, p, p->type);
2103  }
2104  int sz = int(xdir);
2105  if (sz > 0) {
2106  xval = new double[sz];
2107 
2108  hoc_pushpx(&xdir);
2109  hoc_pushpx(xval);
2110  if (f->type() == BBSS_IO::IN) { // restore
2111  xdir = 1.;
2112  f->d(sz, xval);
2113  if (memb_func[p->type].is_point) {
2114  hoc_call_ob_proc(pp->ob, ssi[p->type].callback, narg);
2115  hoc_xpop();
2116  } else {
2117  nrn_call_mech_func(ssi[p->type].callback, narg, p, p->type);
2118  }
2119  } else {
2120  xdir = 0.;
2121  if (memb_func[p->type].is_point) {
2122  hoc_call_ob_proc(pp->ob, ssi[p->type].callback, narg);
2123  hoc_xpop();
2124  } else {
2125  nrn_call_mech_func(ssi[p->type].callback, narg, p, p->type);
2126  }
2127  f->d(sz, xval);
2128  }
2129  delete[] xval;
2130  }
2131  }
2132  if (debug) {
2133  sprintf(dbuf, "Leave mech(prop type %d)", p->type);
2134  PDEBUG;
2135  }
2136 }
2137 
2139  if (debug) {
2140  sprintf(dbuf, "Enter netrecv_pp(pp prop type %d)", pp->prop->type);
2141  PDEBUG;
2142  }
2143  char buf[1000];
2144  sprintf(buf, "%s", hoc_object_name(pp->ob));
2145  f->s(buf);
2146 
2147  // associated NetCon, and queue SelfEvent
2148  DEList *dl, *dl1;
2149  const auto& dliter = pp2de->find(pp);
2150  if (dliter == pp2de->end()) {
2151  dl = 0;
2152  } else {
2153  dl = dliter->second;
2154  }
2155  int cnt = 0;
2156  // dl has the NetCons first then the SelfEvents
2157  // NetCon
2158  for (cnt = 0, dl1 = dl; dl1 && dl1->de->type() == NetConType; dl1 = dl1->next) {
2159  ++cnt;
2160  }
2161  f->i(cnt, 1);
2162  sprintf(buf, "NetCon");
2163  f->s(buf, 1);
2164  for (; dl && dl->de->type() == NetConType; dl = dl->next) {
2165  NetCon* nc = (NetCon*) dl->de;
2166  f->d(nc->cnt_, nc->weight_);
2167  if (f->type() != BBSS_IO::IN) { // writing, counting
2168  DblList* db = 0;
2169  int j = 0;
2170  if (nc2dblist) {
2171  const auto& dbiter = nc2dblist->find(nc);
2172  if (dbiter != nc2dblist->end()) {
2173  db = dbiter->second;
2174  j = db->size();
2175  f->i(j);
2176  for (int i = 0; i < j; ++i) {
2177  double x = (*db)[i];
2178  f->d(1, x);
2179  }
2180  } else {
2181  f->i(j);
2182  }
2183  } else {
2184  f->i(j);
2185  }
2186  int has_stim = 0;
2187  if (nc->src_ && nc->src_->osrc_ && nc->src_->gid_ < 0) {
2188  // save the associated local stimulus
2189  has_stim = 1;
2190  f->i(has_stim, 1);
2191  Point_process* pp = ob2pntproc(nc->src_->osrc_);
2192  mech(pp->prop);
2193  } else {
2194  f->i(has_stim, 1);
2195  }
2196  } else { // reading
2197  int j = 0;
2198  f->i(j);
2199  for (int i = 0; i < j; ++i) {
2200  double x;
2201  f->d(1, x);
2202  x = binq_time(x);
2203  nrn_netcon_event(nc, x);
2204  }
2205  int has_stim = 0;
2206  f->i(has_stim);
2207  if (has_stim) {
2208  assert((nc->src_ && nc->src_->osrc_ && nc->src_->gid_ < 0) == has_stim);
2209  Point_process* pp = ob2pntproc(nc->src_->osrc_);
2210  mech(pp->prop);
2211  }
2212  }
2213  }
2214  // Count SelfEvents. At this point, for restore, there are no
2215  // SelfEvents (so cnt is 0) because the queue has been cleared.
2216  for (cnt = 0, dl1 = dl; dl1 && dl1->de->type() == SelfEventType; dl1 = dl1->next) {
2217  ++cnt;
2218  }
2219  f->i(cnt);
2220  // SelfEvent existence depends on state. For reading the queue
2221  // is empty. So count and save is different from restore.
2222 
2223  if (f->type() != BBSS_IO::IN) {
2224  for (; dl && dl->de->type() == SelfEventType; dl = dl->next) {
2225  SEWrap* sew = (SEWrap*) dl->de;
2226  sprintf(buf, "SelfEvent");
2227  f->s(buf);
2228  f->d(1, sew->se->flag_);
2229  f->d(1, sew->tt);
2230  f->i(sew->ncindex);
2231  int moff = -1;
2232  if (sew->se->movable_) {
2233  moff = (Datum*) (sew->se->movable_) - pp->prop->dparam;
2234  }
2235  f->i(moff);
2236  }
2237  } else { // restore
2238  // For restore it is necessary to create the proper SelfEvents.
2239  // since the queue has been cleared.
2240  for (int i = 0; i < cnt; ++i) {
2241  int ncindex, moff;
2242  double flag, tt, *w;
2243  f->s(buf);
2244  f->d(1, flag);
2245  f->d(1, tt);
2246  f->i(ncindex);
2247  f->i(moff);
2248  void** movable = NULL;
2249  TQItem* tqi;
2250  // new SelfEvent item mostly filled in.
2251  // But starting out with NULL weight vector and
2252  // flag=1 so that tqi->data is the new SelfEvent
2253  net_send((void**) &tqi, NULL, pp, tt, 1.0);
2254  assert(tqi && tqi->data_ && ((DiscreteEvent*) tqi->data_)->type() == SelfEventType);
2255  SelfEvent* se = (SelfEvent*) tqi->data_;
2256  se->flag_ = flag;
2257  if (moff >= 0) {
2258  movable = &(pp->prop->dparam[moff]._pvoid);
2259  if (flag == 1) {
2260  *movable = tqi;
2261  }
2262  se->movable_ = movable;
2263  }
2264  if (ncindex == -1) {
2265  w = NULL;
2266  } else {
2267  int j;
2268  for (j = 0, dl1 = dliter->second; j < ncindex; ++j, dl1 = dl1->next) {
2269  ;
2270  }
2271  assert(dl1 && dl1->de->type() == NetConType);
2272  w = ((NetCon*) dl1->de)->weight_;
2273  }
2274  se->weight_ = w;
2275  }
2276  }
2277  if (debug) {
2278  sprintf(dbuf, "Leave netrecv_pp(pp prop type %d)", pp->prop->type);
2279  PDEBUG;
2280  }
2281 }
2282 
2283 static int giddest_size;
2284 static int* giddest;
2285 static int* tsdest_cnts;
2286 static double* tsdest;
2287 
2288 // input scnt, sdipl ; output rcnt, rdispl
2289 static void all2allv_helper(int* scnt, int* sdispl, int* rcnt, int* rdispl) {
2290  int i;
2291  int np = nrnmpi_numprocs;
2292  int* c = new int[np];
2293  rdispl[0] = 0;
2294  for (i = 0; i < np; ++i) {
2295  c[i] = 1;
2296  rdispl[i + 1] = rdispl[i] + c[i];
2297  }
2298  nrnmpi_int_alltoallv(scnt, c, rdispl, rcnt, c, rdispl);
2299  delete[] c;
2300  rdispl[0] = 0;
2301  for (i = 0; i < np; ++i) {
2302  rdispl[i + 1] = rdispl[i] + rcnt[i];
2303  }
2304 }
2305 
2306 static void all2allv_int2(int* scnt, int* sdispl, int* gidsrc, int* ndsrc) {
2307 #if NRNMPI
2308  int np = nrnmpi_numprocs;
2309  int* rcnt = new int[np];
2310  int* rdispl = new int[np + 1];
2311  all2allv_helper(scnt, sdispl, rcnt, rdispl);
2312 
2313  giddest = 0;
2314  tsdest_cnts = 0;
2315  giddest_size = rdispl[np];
2316  if (giddest_size) {
2317  giddest = new int[giddest_size];
2318  tsdest_cnts = new int[giddest_size];
2319  }
2320  nrnmpi_int_alltoallv(gidsrc, scnt, sdispl, giddest, rcnt, rdispl);
2321  nrnmpi_int_alltoallv(ndsrc, scnt, sdispl, tsdest_cnts, rcnt, rdispl);
2322 
2323  delete[] rcnt;
2324  delete[] rdispl;
2325 #else
2326  assert(0);
2327 #endif
2328 }
2329 
2330 static void all2allv_dbl1(int* scnt, int* sdispl, double* tssrc) {
2331 #if NRNMPI
2332  int size;
2333  int np = nrnmpi_numprocs;
2334  int* rcnt = new int[np];
2335  int* rdispl = new int[np + 1];
2336  all2allv_helper(scnt, sdispl, rcnt, rdispl);
2337 
2338  tsdest = 0;
2339  size = rdispl[np];
2340  if (size) {
2341  tsdest = new double[size];
2342  }
2343  nrnmpi_dbl_alltoallv(tssrc, scnt, sdispl, tsdest, rcnt, rdispl);
2344 
2345  delete[] rcnt;
2346  delete[] rdispl;
2347 #else
2348  assert(0);
2349 #endif
2350 }
2351 
2352 static void scatteritems() {
2353  // since gid queue items with the same gid are scattered on
2354  // many processors, we send all the gid,tscnt pairs (and tslists
2355  // with undelivered NetCon counts)
2356  // to the round-robin host (we do not know the gid owner host yet).
2357  int i, host;
2358  src2send_cnt = 0;
2359  src2send.reset( new Int2DblList());
2360  src2send->reserve(1000);
2362  // if event on queue at t we will not be able to decide whether or
2363  // not it should be delivered during restore.
2364  // The assert was moved into mk_presyn_info since this function is
2365  // reused by bbss_queuecheck and the error in that context will
2366  // be analyzed there.
2367  // assert(tq->least_t() > nrn_threads->_t);
2368  callback_mode = 1;
2370  // space inefficient but simple support analogous to pc.all2all
2371  int* gidsrc = 0;
2372  int* ndsrc = 0; // count for each DblList, parallel to gidsrc
2373  double* tssrc = 0; // all the doubles (and undelivered NetCon counts) in every DblList
2374  if (src2send_cnt) {
2375  int ndsrctotal = 0;
2376  gidsrc = new int[src2send_cnt];
2377  ndsrc = new int[src2send_cnt];
2378  for (const auto& pair: *src2send) {
2379  ndsrctotal += pair.second->size();
2380  }
2381  tssrc = new double[ndsrctotal];
2382  }
2383  int* off = new int[nrnmpi_numprocs + 1]; // offsets for gidsrc and ndsrc
2384  int* cnts = new int[nrnmpi_numprocs]; // dest host counts for gidsrc and ndsrc
2385  int* doff = new int[nrnmpi_numprocs + 1]; // offsets for doubles
2386  int* dcnts = new int[nrnmpi_numprocs + 1]; // dest counts of the doubles
2387  for (i = 0; i < nrnmpi_numprocs; ++i) {
2388  cnts[i] = 0;
2389  dcnts[i] = 0;
2390  }
2391  // counts to send to each destination rank
2392  for (const auto& pair: *src2send) {
2393  int gid = pair.first;
2394  int host = gid % nrnmpi_numprocs;
2395  ++cnts[host];
2396  dcnts[host] += pair.second->size();
2397  }
2398  // offsets
2399  off[0] = 0;
2400  doff[0] = 0;
2401  for (i = 0; i < nrnmpi_numprocs; ++i) {
2402  off[i + 1] = off[i] + cnts[i];
2403  doff[i + 1] = doff[i] + dcnts[i];
2404  }
2405  // what to send to each destination. Note dcnts and ndsrc are NOT the same.
2406  for (i = 0; i < nrnmpi_numprocs; ++i) {
2407  cnts[i] = 0;
2408  dcnts[i] = 0;
2409  }
2410  for (const auto& pair: *src2send) {
2411  const auto dl = pair.second;
2412  int gid = pair.first;
2413  host = gid % nrnmpi_numprocs;
2414  gidsrc[off[host] + cnts[host]] = gid;
2415  ndsrc[off[host] + cnts[host]++] = int(dl->size());
2416  for (size_t i = 0; i < dl->size(); ++i) {
2417  tssrc[doff[host] + dcnts[host]++] = (*dl)[i];
2418  }
2419  }
2420  for (const auto& pair: *src2send) {
2421  delete pair.second;
2422  }
2423  src2send.reset();
2424 
2425  if (nrnmpi_numprocs > 1) {
2426  all2allv_int2(cnts, off, gidsrc, ndsrc);
2427  all2allv_dbl1(dcnts, doff, tssrc);
2428  if (gidsrc) {
2429  delete[] gidsrc;
2430  delete[] ndsrc;
2431  delete[] tssrc;
2432  }
2433  } else {
2434  giddest_size = cnts[0];
2435  giddest = gidsrc;
2436  tsdest_cnts = ndsrc;
2437  tsdest = tssrc;
2438  }
2439  delete[] cnts;
2440  delete[] off;
2441  delete[] dcnts;
2442  delete[] doff;
2443 }
2444 
2445 static void allgatherv_helper(int cnt, int* rcnt, int* rdspl) {
2446  nrnmpi_int_allgather(&cnt, rcnt, 1);
2447  rdspl[0] = 0;
2448  for (int i = 0; i < nrnmpi_numprocs; ++i) {
2449  rdspl[i + 1] = rdspl[i] + rcnt[i];
2450  }
2451 }
2452 
2453 static void spikes_on_correct_host(int cnt,
2454  int* g,
2455  int* dcnts,
2456  int tscnt,
2457  double* ts,
2458  Int2DblList* m) {
2459  // tscnt is the sum of the dcnts.
2460  // i.e. the send times and undelivered NetCon counts.
2461  int i, rsize, *rg = NULL, *rtscnts = NULL;
2462  double* rts = NULL;
2463  // not at all space efficient
2464  if (nrnmpi_numprocs > 1) {
2465  int* cnts = new int[nrnmpi_numprocs];
2466  int* dspl = new int[nrnmpi_numprocs + 1];
2467  allgatherv_helper(cnt, cnts, dspl);
2468  rsize = dspl[nrnmpi_numprocs];
2469  if (rsize) {
2470  rg = new int[rsize];
2471  rtscnts = new int[rsize];
2472  }
2473  nrnmpi_int_allgatherv(g, rg, cnts, dspl);
2474  nrnmpi_int_allgatherv(dcnts, rtscnts, cnts, dspl);
2475 
2476  allgatherv_helper(tscnt, cnts, dspl);
2477  rts = new double[dspl[nrnmpi_numprocs]];
2478  nrnmpi_dbl_allgatherv(ts, rts, cnts, dspl);
2479 
2480  delete[] cnts;
2481  delete[] dspl;
2482  } else {
2483  rsize = cnt;
2484  rg = g;
2485  rtscnts = dcnts;
2486  rts = ts;
2487  }
2488  int tsoff = 0;
2489  for (i = 0; i < rsize; ++i) {
2490  if (nrn_gid_exists(rg[i])) {
2491  DblList* dl = new DblList();
2492  dl->reserve(rtscnts[i]);
2493  (*m)[rg[i]] = dl;
2494  for (int j = 0; j < rtscnts[i]; ++j) {
2495  dl->push_back(rts[tsoff + j]);
2496  }
2497  }
2498  tsoff += rtscnts[i];
2499  }
2500  if (nrnmpi_numprocs > 1) {
2501  if (rg)
2502  delete[] rg;
2503  if (rtscnts)
2504  delete[] rtscnts;
2505  if (rts)
2506  delete[] rts;
2507  }
2508 }
2509 
2510 static void construct_presyn_queue() {
2511  // almost all the old mk_presyn_info factored into here since
2512  // a presyn_queue is optionally needed for check_queue()
2513 
2514  // note that undelivered netcon count is interleaved with tsend
2515  // in the DblList of the Int2DblList
2516  int tscnt, i;
2517  DblList* dl;
2518  if (presyn_queue) {
2519  del_presyn_info();
2520  }
2521  nc2dblist.reset(new NetCon2DblList());
2522  nc2dblist->reserve(20);
2523  scatteritems();
2524  int cnt = giddest_size;
2525  std::unique_ptr<Int2DblList> m{new Int2DblList()};
2526  m->reserve(cnt + 1);
2527  int mcnt = 0;
2528  int mdcnt = 0;
2529  int its = 0;
2530  for (i = 0; i < cnt; ++i) {
2531  int gid = giddest[i];
2532  tscnt = tsdest_cnts[i];
2533  const auto& dliter = m->find(gid);
2534  if (dliter != m->end()) {
2535  dl = dliter->second;
2536  } else {
2537  dl = new DblList();
2538  (*m)[gid] = dl;
2539  ++mcnt;
2540  }
2541  // add the initiation time(s) if they are unique ( > .1)
2542  // and also accumulate the undelivered netcon counts
2543  // otherwise assert if not identical ( > 1e-12)
2544  for (int k = 0; k < tscnt; k += 2) {
2545  double t1 = tsdest[its++];
2546  int inccnt = tsdest[its++];
2547  int add = 1;
2548  // can't hurt to test the ones just added
2549  // no thought given to efficiency
2550  // Actually, need to test to accumulate
2551  for (int j = 0; j < dl->size(); j += 2) {
2552  double dt = fabs((*dl)[j] - t1);
2553  if (dt < 1e-12) {
2554  (*dl)[j + 1] += inccnt;
2555  add = 0;
2556  break;
2557  } else if (dt < .1) {
2558  assert(0);
2559  }
2560  }
2561  if (add) {
2562  dl->push_back(t1);
2563  dl->push_back(inccnt);
2564  mdcnt += 2;
2565  }
2566  }
2567  }
2568  if (giddest) {
2569  delete[] giddest;
2570  delete[] tsdest_cnts;
2571  delete[] tsdest;
2572  }
2573  // each host now has a portion of the (gid, ts) spike pairs
2574  // (Actually (gid, list of (ts, undelivered NetCon count)) map.)
2575  // but the pairs are not on the hosts that own the gid.
2576  // The major thing that is accomplished so far is that the
2577  // up to thousands of Netcon events on the queues of thousands
2578  // of host are now a single PreSyn event on a single host.
2579  // We could save them all in separate area and read them all
2580  // and send only when a host owns the gid, but we wish
2581  // to keep the spike sent info with the cell info for the gid.
2582  // We next need to do something analogous to the allgather send
2583  // so each host can examine every spike and pick out the ones
2584  // which were sent from that host. That becomes the true
2585  // presyn_queue map.
2586  int* gidsrc = 0;
2587  int* tssrc_cnt = 0;
2588  double* tssrc = 0;
2589  if (mcnt) {
2590  gidsrc = new int[mcnt];
2591  tssrc_cnt = new int[mcnt];
2592  tssrc = new double[mdcnt];
2593  }
2594  mcnt = 0;
2595  mdcnt = 0;
2596  for (const auto& pair: *m) {
2597  auto dl = pair.second;
2598  gidsrc[mcnt] = pair.first;
2599  tssrc_cnt[mcnt] = dl->size();
2600  for (int i = 0; i < dl->size(); ++i) {
2601  tssrc[mdcnt++] = (*dl)[i];
2602  }
2603  ++mcnt;
2604  delete dl;
2605  }
2606  presyn_queue.reset(new Int2DblList());
2607  presyn_queue->reserve(127);
2608  spikes_on_correct_host(mcnt, gidsrc, tssrc_cnt, mdcnt, tssrc, presyn_queue.get());
2609  if (gidsrc) {
2610  delete[] gidsrc;
2611  delete[] tssrc_cnt;
2612  delete[] tssrc;
2613  }
2614 }
2615 
2616 void BBSaveState::mk_presyn_info() { // also the NetCon* to tdelivery map
2617  if (f->type() != BBSS_IO::IN) { // only when saving or counting
2618  // if event on queue at t we will not be able to decide
2619  // whether or not it should be delivered during restore.
2621 
2622  TQItem* tqi = tq->least();
2623  int dtype = tqi ? ((DiscreteEvent*) (tqi->data_))->type() : 0;
2624  assert(tq->least_t() > nrn_threads->_t || dtype == NetParEventType);
2626  }
2627 }
2628 
2630  if (debug) {
2631  sprintf(dbuf, "Enter possible_presyn()");
2632  PDEBUG;
2633  }
2634  char buf[100];
2635  int i;
2636  double ts;
2637  // first the presyn state associated with this gid
2638  if (nrn_gid_exists(gid) < 2) {
2639  if (f->type() == BBSS_IO::IN) {
2640  // if reading then skip what was written
2641  i = 0;
2642  f->i(i);
2643  if (i == 1) { // skip state
2644  int j;
2645  double x;
2646  sprintf(buf, "PreSyn");
2647  f->s(buf, 1);
2648  f->i(j);
2649  f->d(1, x);
2650  }
2651  } else {
2652  i = -1;
2653  f->i(i);
2654  }
2655  } else {
2656  PreSyn* ps = nrn_gid2presyn(gid);
2657  i = (ps->ssrc_ != 0 ? 1 : -1); // does it have state
2658  f->i(i, 1);
2659  int output_index = ps->output_index_;
2660  f->i(output_index);
2661  if (output_index >= 0 && i == 1) {
2662  sprintf(buf, "PreSyn");
2663  f->s(buf, 1);
2664  int j = (ps->flag_ ? 1 : 0);
2665  double th = ps->valthresh_;
2666  f->i(j);
2667  f->d(1, th);
2668  if (ps->output_index_ >= 0) {
2669  ps->flag_ = j;
2670  ps->valthresh_ = th;
2671  }
2672 #if 0
2673  f->d(1, ps->valold_);
2674  f->d(1, ps->told_);
2675  // ps->qthresh_ handling unimplemented but would not be needed
2676  // unless cvode.condition_order(2) when cvode active.
2677 #endif
2678  }
2679  }
2680  // next the possibility that a spike derived from this presyn
2681  // is on the queue.
2682  if (f->type() != BBSS_IO::IN) { // save or count
2683  DblList* dl;
2684  const auto& dliter = presyn_queue->find(gid);
2685  if (dliter != presyn_queue->end()) {
2686  dl = dliter->second;
2687  f->i(gid);
2688  i = dl->size(); // ts and undelivered netcon counts
2689  f->i(i);
2690  for (i = 0; i < dl->size(); i += 2) {
2691  ts = (*dl)[i];
2692  f->d(1, ts);
2693  int unc = (*dl)[i + 1];
2694  f->i(unc);
2695  }
2696  } else {
2697  i = -1;
2698  f->i(i);
2699  }
2700  } else { // restore
2701  f->i(i); // gid
2702  if (i >= 0) {
2703  int cnt, unc;
2704  // assert (gid == i);
2705  if (gid == i) {
2706  f->i(cnt); // both the ts and undelivered netcon counts
2707 
2708  // while re-issuing the PreSyn send, we do not want
2709  // any spike recording to take place. So, what are
2710  // the current Vector sizes, if used, so we can put
2711  // them back. This presumes we are recording only
2712  // from PreSyn with an output_gid.
2713 #if 1 // if set to 0 comment out asserts below
2714  // The only reason we save here is to allow a
2715  // assertion test when restoring the original
2716  // record vector size.
2717  int sz1 = -1;
2718  int sz2 = -1;
2719  PreSyn* ps = nrn_gid2presyn(gid);
2720  if (ps->tvec_) {
2721  sz1 = ps->tvec_->size();
2722  }
2723  if (ps->idvec_) {
2724  sz2 = ps->idvec_->size();
2725  }
2726 #endif
2727 
2728 #if QUEUECHECK
2729  // map from gid to unc for later checking
2730  if (!queuecheck_gid2unc) {
2731  queuecheck_gid2unc.reset(new Int2DblList());
2732  queuecheck_gid2unc->reserve(1000);
2733  }
2734  DblList* dl = new DblList();
2735  (*queuecheck_gid2unc)[i] = dl;
2736 #endif
2737  for (int j = 0; j < cnt; j += 2) {
2738  f->d(1, ts);
2739  f->i(unc); // expected undelivered NetCon count
2740  nrn_fake_fire(gid, ts, 2); // only owned by this
2741 #if QUEUECHECK
2742  dl->push_back(ts);
2743  dl->push_back(unc);
2744 #endif
2745  }
2746 
2747  // restore spike record sizes.
2748  if (ps->tvec_) {
2749  int sz = ps->tvec_->size() - cnt / 2;
2750  assert(sz == sz1);
2751  ps->tvec_->resize(sz);
2752  }
2753  if (ps->idvec_) {
2754  int sz = ps->idvec_->size() - cnt / 2;
2755  assert(sz == sz2);
2756  ps->idvec_->resize(sz);
2757  }
2758  } else {
2759  // skip -> file has undelivered spikes, but this is not the cell that
2760  // deals with them
2761  f->i(cnt);
2762  for (int j = 0; j < cnt; j += 2) {
2763  f->d(1, ts);
2764  f->i(unc);
2765  }
2766  }
2767  }
2768  }
2769  if (debug) {
2770  sprintf(dbuf, "Leave possible_presyn()");
2771  PDEBUG;
2772  }
2773 }
2774 
2775 #if QUEUECHECK
2776 static void bbss_queuecheck() {
2778  if (queuecheck_gid2unc)
2779  for (const auto& pair: *queuecheck_gid2unc) {
2780  auto gid = pair.first;
2781  auto dl = pair.second;
2782  DblList* dl2;
2783  const auto& dl2iter = presyn_queue->find(gid);
2784  if (dl2iter != presyn_queue->end()) {
2785  dl2 = dl2iter->second;
2786  if (dl->size() == dl2->size()) {
2787  for (int i = 0; i < dl->size(); i += 2) {
2788  if ((fabs((*dl)[i] - (*dl2)[i]) > 1e-12) || (*dl)[i + 1] != (*dl2)[i + 1]) {
2789  printf(
2790  "error: gid=%d expect t=%g %d but queue contains t=%g %d "
2791  "tdiff=%g\n",
2792  gid,
2793  (*dl)[i],
2794  int((*dl)[i + 1]),
2795  (*dl2)[i],
2796  int((*dl2)[i + 1]),
2797  (*dl)[i] - (*dl2)[i]);
2798  }
2799  }
2800  } else {
2801  printf("error: gid=%d distinct delivery times, expect %ld, actual %ld\n",
2802  gid,
2803  dl->size() / 2,
2804  dl2->size() / 2);
2805  }
2806  } else {
2807  printf("error: gid=%d expect spikes but none on queue\n", gid);
2808  for (int i = 0; i < dl->size() - 1; i += 2) {
2809  printf(" %g %d", (*dl)[i], int((*dl)[i + 1]));
2810  }
2811  printf("\n");
2812  }
2813  }
2814  for (const auto& pair: *presyn_queue) {
2815  auto gid = pair.first;
2816  auto dl2 = pair.second;
2817  DblList* dl;
2818  const auto& dliter = presyn_queue->find(gid);
2819  if (dliter == presyn_queue->end()) {
2820  dl = dliter->second;
2821  printf("error: gid=%d expect no spikes but some on queue\n", gid);
2822  for (int i = 0; i < int(dl2->size()) - 1; i += 2) {
2823  printf(" %g %d", (*dl)[i], int((*dl)[i + 1]));
2824  }
2825  printf("\n");
2826  }
2827  }
2828 
2829  // cleanup
2830  if (queuecheck_gid2unc) {
2831  for (const auto& pair: *queuecheck_gid2unc) {
2832  delete pair.second;
2833  }
2834  queuecheck_gid2unc.reset();
2835  }
2836  del_presyn_info();
2837 }
2838 #endif /*QUEUECHECK*/
static int nrnmpi_int_allmax(int x)
virtual void d(int n, double &p)
IvocVect * tvec_
Definition: netcon.h:267
int var_type(Symbol *) const
Definition: ndatclas.cpp:153
Object * nrn_gid2obj(int gid)
Definition: netpar.cpp:1495
Section * nrn_section_exists(char *name, int index, Object *cell)
Definition: cabcode.cpp:2409
int cnt_
Definition: netcon.h:109
Definition: hocdec.h:84
void gid2buffer(int gid, char *buffer, int size)
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
int param_size
Definition: section.h:217
#define nrn_assert(ex)
Definition: nrnassrt.h:35
size_t hoc_total_array_data(Symbol *s, Objectdata *obd)
Definition: hoc_oop.cpp:106
static double save_test_bin(void *v)
static void all2allv_dbl1(int *scnt, int *sdispl, double *tssrc)
double * param
Definition: section.h:218
struct Prop * prop
Definition: section.h:62
BBSS_TxtFileIn(const char *)
virtual void cpy(int size, char *cp)
#define assert(ex)
Definition: hocassrt.h:26
static char dbuf[1024]
bool flag_
Definition: netcon.h:180
static void nrnmpi_int_allgatherv(int *s, int *r, int *n, int *dspl)
Object * nrn_sec2cell(Section *)
Definition: cabcode.cpp:224
int counts(int **gids, int **counts)
short nnode
Definition: section.h:41
virtual void i(int &j, int chk=0)
void(* ReceiveFunc)(Point_process *, double *, double)
static void bbss_remove_delivered()
static void destruct(void *v)
void * _pvoid
Definition: hocdec.h:186
#define hocSEC(q)
Definition: hoclist.h:66
struct Node * parentnode
Definition: section.h:50
virtual void d(int n, double &p)=0
static std::unique_ptr< Int2DblList > src2send
Definition: netcon.h:232
#define NODEV(n)
Definition: section.h:114
#define Vect
Definition: ivocvect.h:14
void nrnmpi_abort(int errcode)
Definition: nrnmpi.cpp:205
virtual void s(char *cp, int chk=0)
void nrn_play_init()
Definition: cvodestb.cpp:75
if(status)
std::vector< double > DblList
short itemtype
Definition: hoclist.h:52
static void nrn_spike_exchange(NrnThread *)
void * nrn_opaque_obj2pyobj(Object *ho)
Definition: hoc_oop.cpp:2232
virtual void i(int &j, int chk=0)
void cell(Object *)
void mk_base2spgid()
static std::unique_ptr< Int2Int > base2spgid
hoc_Item * net_cvode_instance_psl()
Definition: netcvode.cpp:301
virtual void skip(int n)
#define ITERATE(itm, lst)
Definition: model.h:25
#define g
Definition: passive0.cpp:23
size_t size() const
Definition: ivocvect.h:43
static void all2allv_helper(int *scnt, int *sdispl, int *rcnt, int *rdispl)
BBSS_BufferIn(char *buffer, int size)
PlayRecList * net_cvode_instance_prl()
Definition: netcvode.cpp:305
static double binq_time(double tt)
static std::unique_ptr< Int2DblList > queuecheck_gid2unc
virtual ~BBSS_TxtFileOut()
static double restore_test(void *v)
Symbol * hoc_lookup(const char *)
int prop_index(const Symbol *) const
Definition: ndatclas.cpp:215
Symlist * symtable
Definition: hocdec.h:196
int bytecntbin()
void
static void base2spgid_item(int spgid, Object *obj)
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
static void pycell_name2sec_maps_clear()
void shift_bin(double t)
Definition: sptbinq.h:81
static int first
Definition: fmenu.cpp:186
void BBSaveState_reg()
static TQItemList * tq_removal_list
int bytecntasc()
int output_index_
Definition: netcon.h:276
static std::unique_ptr< Int2DblList > presyn_queue
#define STATE
Definition: membfunc.h:71
std::unordered_map< int, DblList * > Int2DblList
static void del_presyn_info()
static StateStructInfo * ssi
static double restore_gid(void *v)
static bool use_gidcompress_
char * name
Definition: model.h:72
static SEWrapList * sewrap_list
Memb_func * memb_func
Definition: init.cpp:161
nd
Definition: treeset.cpp:893
Object * osrc_
Definition: netcon.h:265
Point_process * target_
Definition: netcon.h:106
static int callback_mode
static int narg()
Definition: ivocvect.cpp:135
BBSS_TxtFileOut(const char *)
#define v
Definition: md1redef.h:4
IvocVect * idvec_
Definition: netcon.h:268
Section * ssrc_
Definition: netcon.h:266
Symlist * hoc_built_in_symlist
Definition: symbol.cpp:39
Item * next(Item *item)
Definition: list.cpp:95
std::unordered_map< NetCon *, DblList * > NetCon2DblList
#define chk(i)
Definition: fmenu.cpp:189
#define PreSynType
Definition: netcon.h:47
int ithread_
Definition: netcon.h:379
double t_
Definition: bbtqueue.h:18
SelfEvent * se
void nrn_netcon_event(NetCon *, double)
Definition: netcvode.cpp:162
virtual void skip(int)
int i
Definition: hocdec.h:179
static philox4x32_key_t k
Definition: nrnran123.cpp:11
static void ssi_def()
sprintf(buf," if (secondorder) {\ " int _i;\" " for(_i=0;_i< %d;++_i) {\" " _p[_slist%d[_i]]+=dt *_p[_dlist%d[_i]];\" " }}\", numeqn, listnum, listnum)
Definition: bbtqueue.h:6
static void bbss_early(double td, TQItem *tq)
virtual Type type()=0
double delay_
Definition: netcon.h:104
double tt
hoc_List * olist
Definition: hocdec.h:203
#define e
Definition: passive0.cpp:24
static int src2send_cnt
#define gargstr
Definition: hocdec.h:14
int sectionsize(Section *)
PreSyn * src_
Definition: netcon.h:105
virtual ~BBSS_BufferOut()
double tbin()
Definition: sptbinq.h:82
static void allgatherv_helper(int cnt, int *rcnt, int *rdspl)
short type
Definition: section.h:215
virtual ~BBSaveState()
virtual void s(char *cp, int chk=0)=0
void init()
Definition: init.cpp:169
double dt
Definition: init.cpp:123
void forall_callback(void(*)(const TQItem *, int))
Definition: bbtqueue.cpp:120
virtual void savestate_restore(double deliverytime, NetCvode *)
Definition: netpar.cpp:305
double least_t()
Definition: bbtqueue.cpp:136
double _dt
Definition: multicore.h:60
int bytecnt()
virtual void a(int)
virtual void i(int &j, int chk=0)
static int * tsdest_cnts
static void bbss_restore_begin()
TQueue * net_cvode_instance_event_queue(NrnThread *)
Definition: netcvode.cpp:297
void hoc_execerr_ext(const char *fmt,...)
printf style specification of hoc_execerror message.
Definition: fileio.cpp:958
static double cell(void *v)
Definition: ocbbs.cpp:573
TQItem * least()
Definition: bbtqueue.cpp:145
virtual void i(int &j, int chk=0)
static double ref(void *v)
Definition: ocbox.cpp:377
int const size_t const size_t n
Definition: nrngsl.h:12
virtual void i(int &j, int chk=0)=0
static void construct_presyn_queue()
int nrnmpi_numprocs
Point_process * ob2pntproc(Object *)
Definition: hocmech.cpp:88
static void * cons(Object *)
#define PDEBUG
virtual Type type()
_CONST char * s
Definition: system.cpp:74
static double save_request(void *v)
NrnThread * nrn_threads
Definition: multicore.cpp:45
struct DEList * next
void class2oc(const char *, void *(*cons)(Object *), void(*destruct)(void *), Member_func *, int(*checkpoint)(void **), Member_ret_obj_func *, Member_ret_str_func *)
Definition: hoc_oop.cpp:1581
Section ** secorder
Definition: solve.cpp:77
void hoc_obj_unref(Object *obj)
Definition: hoc_oop.cpp:1998
virtual void apply(BBSS_IO *io)
static int indx
Definition: deriv.cpp:240
virtual ~BBSS_BufferIn()
void bbss_save(void *bbss, int gid, char *buffer, int sz)
static void nrnmpi_barrier()
static double ppignore(void *v)
static void all2allv_int2(int *scnt, int *sdispl, int *gidsrc, int *ndsrc)
Prop * prop
Definition: section.h:264
#define printf
Definition: mwprefix.h:26
void * bbss_buffer_counts(int *len, int **gids, int **sizes, int *global_size)
int
Definition: nrnmusic.cpp:71
const char * secname(Section *sec)
Definition: cabcode.cpp:1787
virtual void s(char *cp, int chk=0)
void section(Section *)
double valold_
Definition: netcon.h:177
void bbss_restore_global(void *bbss, char *buffer, int sz)
virtual int type()
Definition: netcon.h:69
static const char * fname(const char *name)
Definition: nrnbbs.cpp:108
Symbol * first_var()
Definition: ndatclas.cpp:127
static Member_func members[]
#define PROP_PY_INDEX
Definition: section.h:211
int n_memb_func
Definition: init.cpp:471
double hoc_xpop(void)
void ** movable_
Definition: netcon.h:152
#define cnt
Definition: spt2queue.cpp:19
#define ForAllSections(sec)
Definition: section.h:317
HocStruct hoc_Item * secelm_
Definition: hocdec.h:240
#define NetConType
Definition: netcon.h:45
int is_point_process(Object *)
Definition: point.cpp:405
PreSyn * nrn_gid2presyn(int gid)
Definition: netpar.cpp:1499
static void spikes_on_correct_host(int cnt, int *g, int *dcnts, int tscnt, double *ts, Int2DblList *m)
static SecName2Sec & pycell_name2sec_map(Object *c)
static void nrnmpi_int_alltoallv(int *s, int *scnt, int *sdispl, int *r, int *rcnt, int *rdispl)
virtual Type type()
size_t j
static int * giddest
void gidobj(int basegid)
void(* PFIO)(int, Object *)
Section * sec
Definition: section.h:262
fprintf(stderr, "Don't know the location of params at %p\, pp)
virtual void cpy(int size, char *cp)
Definition: model.h:57
static double restore(void *v)
int section_count
Definition: solve.cpp:76
int v_structure_change
Definition: cvodestb.cpp:99
void del_pp2de()
Definition: section.h:213
char * name
Definition: init.cpp:16
double nrn_call_mech_func(Symbol *s, int narg, Prop *p, int type)
Definition: init.cpp:1060
virtual void d(int n, double &p)
void netrecv_pp(Point_process *)
virtual ~BBSS_Cnt()
Definition: netcon.h:82
double * weight_
Definition: netcon.h:107
Object * ob
Definition: section.h:266
bool nrn_use_bin_queue_
Definition: netcvode.cpp:251
int gid_
Definition: netcon.h:277
int type()
static std::unique_ptr< PP2DE > pp2de
void buffer2gid(int gid, char *buffer, int size)
void mech(Prop *)
DiscreteEvent * de
static int ignored(Prop *p)
void mk_presyn_info()
virtual void s(char *cp, int chk=0)
double flag_
Definition: netcon.h:149
int nrn_gid_exists(int gid)
Definition: netpar.cpp:997
int ifarg(int)
Definition: code.cpp:1562
void(* nrn_binq_enqueue_error_handler)(double, TQItem *)
Definition: sptbinq.cpp:28
void seccontents(Section *)
BBSS_IO * f
Definition: bbsavestate.h:30
void nrn_gidout_iter(PFIO)
Definition: netpar.cpp:1505
std::unordered_map< Point_process *, int > PointProcessMap
static void pycell_name2sec_maps_fill()
Datum * dparam
Definition: section.h:219
void nrn_fake_fire(int gid, double firetime, int fake_out)
Definition: netpar.cpp:867
short * nrn_is_artificial_
Definition: init.cpp:231
void node(Node *)
void clear_event_queue()
Definition: cvodestb.cpp:57
cTemplate ** nrn_pnt_template_
Definition: init.cpp:169
static int usebin_
std::vector< TQItem * > TQItemList
double t
Definition: init.cpp:123
#define OBJ(q)
Definition: hoclist.h:67
static void nrnmpi_int_allgather(int *s, int *r, int n)
Symbol * sym
Definition: hocdec.h:178
static std::unordered_map< void *, SecName2Sec > pycell_name2sec_maps
void section_exist_info(Section *)
void nrn_shape_update()
Definition: treeset.cpp:932
Vect * vector_arg(int i)
Definition: ivocvect.cpp:332
void bbss_save_global(void *bbss, char *buffer, int sz)
#define _AMBIGUOUS
Definition: membfunc.h:101
double _t
Definition: multicore.h:59
virtual void d(int n, double &p)
void fanout(double, NetCvode *, NrnThread *)
Definition: netcvode.cpp:3263
struct hoc_Item * next
Definition: hoclist.h:50
int nrnmpi_myid
virtual ~BBSS_TxtFileIn()
void bbss_restore(void *bbss, int gid, int npiece, char *buffer, int sz)
Symbol * next_var()
Definition: ndatclas.cpp:140
static void bbss_queuecheck()
Definition: hocdec.h:226
HocStruct cTemplate * ctemplate
Definition: hocdec.h:151
static void nrnmpi_dbl_alltoallv(double *s, int *scnt, int *sdispl, double *r, int *rcnt, int *rdispl)
Symbol * hoc_table_lookup(const char *, Symlist *)
Definition: symbol.cpp:60
static double save_gid(void *v)
struct DEList DEList
NetConPList dil_
Definition: netcon.h:261
static int debug
ReceiveFunc * pnt_receive
Definition: init.cpp:171
static BBSaveState * bbss
#define i
Definition: md1redef.h:12
int is_point
Definition: membfunc.h:53
#define c
std::unordered_map< Point_process *, DEList * > PP2DE
struct Prop * next
Definition: section.h:214
void net_send(void **, double *, Point_process *, double, double)
Definition: netcvode.cpp:2340
bool more_var()
Definition: ndatclas.cpp:132
static TQItemList * tq_presyn_fanout
#define SelfEventType
Definition: netcon.h:46
sec
Definition: solve.cpp:885
int cellsize(Object *)
char buf[512]
Definition: init.cpp:13
fabs
Definition: extdef.h:3
virtual Type type()
struct Node ** pnode
Definition: section.h:51
virtual void s(char *cp, int chk=0)
void hoc_pushpx(double *d)
Definition: code.cpp:702
static double save_test(void *v)
#define add
Definition: redef.h:24
static bool use_spikecompress_
void hoc_call_ob_proc(Object *ob, Symbol *sym, int narg)
struct hoc_Item * prev
Definition: hoclist.h:51
virtual Type type()
double told_
Definition: netcon.h:177
static double vector_play_init(void *v)
void * data_
Definition: bbtqueue.h:17
static void nrnmpi_dbl_allgatherv(double *s, double *r, int *n, int *dspl)
BBSS_BufferOut(char *buffer, int size)
union Symbol::@18 u
static double save(void *v)
std::vector< SEWrap * > SEWrapList
void remove(TQItem *)
Definition: bbtqueue.cpp:239
static Node * node(Object *)
Definition: netcvode.cpp:318
double valthresh_
Definition: netcon.h:178
Definition: section.h:132
std::unordered_map< int, int > Int2Int
static std::unique_ptr< PointProcessMap > pp_ignore_map
#define NetParEventType
Definition: netcon.h:51
static int giddest_size
int count
Definition: hocdec.h:202
size_t q
Definition: hocdec.h:176
Prop * prop() const
Definition: ndatclas.cpp:123
static void scatteritems()
static void cb_gidobj(int gid, Object *obj)
Object ** hoc_objgetarg(int)
Definition: code.cpp:1568
#define DEBUG
FILE * fopen()
virtual void i(int &j, int chk=0)
void possible_presyn(int gid)
void bbss_restore_done(void *bbss)
SEWrap(const TQItem *, DEList *)
void node01(Section *, Node *)
return NULL
Definition: cabcode.cpp:461
static double restore_test_bin(void *v)
std::unordered_map< std::string, Section * > SecName2Sec
static cTemplate * nct
static std::unique_ptr< NetCon2DblList > nc2dblist
virtual Type type()
static double * tsdest
virtual void d(int n, double &p)
short index
Definition: cabvars.h:11
virtual void skip(int)
Definition: bbsavestate.h:22
static void tqcallback(const TQItem *tq, int i)
virtual void s(char *cp, int chk=0)
double delay_
Definition: netcon.h:263
struct Prop * prop
Definition: section.h:151
virtual void pr(const char *, double t, NetCvode *)
Definition: netcvode.cpp:3084
void bbss_save_done(void *bbss)
void resize(size_t n)
Definition: ivocvect.h:47