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 #ifdef 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  }
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);
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;
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  }
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;
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);
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;
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
1164  // key for a cell which was multisplit
1165 
1166 typedef std::unordered_map<int, DblList*> Int2DblList;
1167 static std::unique_ptr<Int2DblList> src2send{new Int2DblList()};
1168 ; // gid to presyn send time map
1169 static int src2send_cnt;
1170 // the DblList was needed in place of just a double because there might
1171 // be several spikes from a single PreSyn (interval between spikes less
1172 // than the maximum NetCon delay for that PreSyn).
1173 // Given a current bug regarding dropped spikes with bin queuing as well
1174 // as the question about the proper handling of a spike with delivery
1175 // time equal to the save state time, it is useful to provide a mechanism
1176 // that allows us to assert that the number of spikes on the queue
1177 // at a save (conceptually the number of NetCon fanned out from the
1178 // PreSyn that have not yet been delivered)
1179 // is identical to the number of spikes on the queue after
1180 // a restore. To help with this, we add a count of the "to be delivered"
1181 // NetCon spikes on the queue to each PreSyn item in the saved file.
1182 // For least effort, given the need to handle counts in the same
1183 // fashion as we handle the presyn send times in the DblList, we merely
1184 // represent (ts, cnt) pairs as adjacent items in the DblList.
1185 // For saving, the undelivered netcon count is always computed. After
1186 // restore this information can be checked against the actual
1187 // netcon count on the queue by defining QUEUECHECK to 1. Note that
1188 // computing the undelivered netcon count involves: 1) each machine
1189 // processing its queue's NetCon and PreSyn spikes using tqcallback
1190 // with callback_mode 1. 2) aggregating the PreSyn counts on some
1191 // machine using scatteritems(). 3) lastly sending the total count per
1192 // PreSyn to the machine that owns the PreSyn gid (see mk_presyn_info).
1193 // This 3-fold process is reused during restore to check the counts ane
1194 // we have factored out the relevant code so it can be used for both
1195 // save and restore (for the latter see bbss_queuecheck()).
1196 #if QUEUECHECK
1197 static std::unique_ptr<Int2DblList> queuecheck_gid2unc;
1198 #endif
1199 
1200 static double binq_time(double tt) {
1201  if (nrn_use_bin_queue_) {
1202  double dt = nrn_threads->_dt;
1203  // For a given event time, return a time which would put it in the
1204  // same bin on restore, that it came from on save.
1205  // Note that, before save, it was put on the queue via BinQ::enqueue
1206  // with int idt = (int)((td - tt_)/nt_dt + 1.e-10);
1207  // where tt_ is the early half step edge time of the current bin.
1208  double t1 = int(tt / dt + 0.5 + 1e-10) * dt;
1209  return t1;
1210  }
1211  return tt;
1212 }
1213 
1214 static void bbss_early(double td, TQItem* tq) {
1215  int type = ((DiscreteEvent*) tq->data_)->type();
1216  // If NetCon, discard. If PreSyn, fanout.
1217  if (type == NetConType) {
1218  return;
1219  } else if (type == PreSynType) {
1220  PreSyn* ps = (PreSyn*) tq->data_;
1222  } else {
1223  assert(0);
1224  }
1225 }
1226 
1227 static void tqcallback(const TQItem* tq, int i) {
1228  int type = ((DiscreteEvent*) tq->data_)->type();
1229  switch (callback_mode) {
1230  case 0: { // the self events
1231  if (type == SelfEventType) {
1232  Point_process* pp = ((SelfEvent*) tq->data_)->target_;
1233  DEList* dl1 = NULL;
1234  SEWrap* sew = 0;
1235  const auto& dl1iter = pp2de->find(pp);
1236  if (dl1iter != pp2de->end()) {
1237  dl1 = dl1iter->second;
1238  sew = new SEWrap(tq, dl1);
1239  } else {
1240  sew = new SEWrap(tq, NULL);
1241  }
1242  if (sew->ncindex == -2) { // ignore the self event
1243  // printf("%d Ignoring a SelfEvent to %s\n", nrnmpi_myid,
1244  // memb_func[pp->prop->type].sym->name);
1245  delete sew;
1246  sew = 0;
1247  }
1248  if (sew) {
1249  sewrap_list->push_back(sew);
1250  DEList* dl = new DEList;
1251  dl->next = 0;
1252  dl->de = sew;
1253  if (dl1) {
1254  while (dl1->next) {
1255  dl1 = dl1->next;
1256  }
1257  dl1->next = dl;
1258  } else {
1259  (*pp2de)[pp] = dl;
1260  }
1261  }
1262  }
1263  } break;
1264 
1265  case 1: { // the NetCon (and PreSyn) events
1266  int srcid, i;
1267  double ts;
1268  PreSyn* ps;
1269  int cntinc; // number on queue to be delivered due to this event
1270  NetCon* nc = 0;
1271  if (type == NetConType) {
1272  nc = (NetCon*) tq->data_;
1273  ps = nc->src_;
1274  ts = tq->t_ - nc->delay_;
1275  cntinc = 1;
1276  } else if (type == PreSynType) {
1277  ps = (PreSyn*) tq->data_;
1278  ts = tq->t_ - ps->delay_;
1279  cntinc = ps->dil_.size();
1280  } else {
1281  return;
1282  }
1283  if (ps) {
1284  if (ps->gid_ >= 0) { // better not be from NetCon.event
1285  srcid = ps->gid_;
1286  DblList* dl;
1287  const auto& dliter = src2send->find(srcid);
1288  if (dliter != src2send->end()) {
1289  dl = dliter->second;
1290  // If delay is long and spiking
1291  // is fast there may be
1292  // another source spike when
1293  // its previous spike is still on
1294  // the queue. So we might have
1295  // to add another initiation time.
1296  // originally there was an assert error here under the assumption that
1297  // all spikes from a PreSyn were delivered before that PreSyn fired
1298  // again. The assumption did not hold for existing Blue Brain models.
1299  // Therefore we extend the algorithm to any number of spikes with
1300  // different initiation times from the same PreSyn. For sanity we
1301  // assume Presyns do not fire more than once every 0.1 ms.
1302  // Unfortunately this possibility makes mpi exchange much more
1303  // difficult as the number of doubles exchanged can be greater than
1304  // the number of src2send gids. Add another initiation time if needed
1305  double m = 1e9; // > .1 add
1306  int im = -1; // otherwise assert < 1e-12
1307  for (i = 0; i < dl->size(); i += 2) {
1308  double x = fabs((*dl)[i] - ts);
1309  if (x < m) {
1310  m = x;
1311  im = i;
1312  }
1313  }
1314  if (m > 0.1) {
1315  dl->push_back(ts);
1316  dl->push_back(cntinc);
1317  } else if (m > 1e-12) {
1318  assert(0);
1319  } else {
1320  (*dl)[im + 1] += cntinc;
1321  }
1322  } else {
1323  dl = new DblList();
1324  dl->push_back(ts);
1325  dl->push_back(cntinc);
1326  (*src2send)[srcid] = dl;
1327  ++src2send_cnt; // distinct PreSyn
1328  }
1329 
1330  } else {
1331  // osrc state should already have been associated
1332  // with the target Point_process and we should
1333  // now associate this event as well
1334  if (ps->osrc_) { // NetStim possibly
1335  // does not matter if NetCon.event
1336  // or if the event was sent from
1337  // the local stimulus. Can't be from
1338  // a PreSyn event since we demand
1339  // that there be only one NetCon
1340  // from this stimulus.
1341  assert(nc);
1342  DblList* db = 0;
1343  const auto& dbiter = nc2dblist->find(nc);
1344  if (dbiter == nc2dblist->end()) {
1345  db = new DblList();
1346  (*nc2dblist)[nc] = db;
1347  } else {
1348  db = dbiter->second;
1349  }
1350  db->push_back(tq->t_);
1351  } else { // assume from NetCon.event
1352  // ps should be unused_presyn
1353  // printf("From NetCon.event\n");
1354  assert(nc);
1355  DblList* db = 0;
1356  const auto& dbiter = nc2dblist->find(nc);
1357  if (dbiter == nc2dblist->end()) {
1358  db = new DblList();
1359  (*nc2dblist)[nc] = db;
1360  } else {
1361  db = dbiter->second;
1362  }
1363  db->push_back(tq->t_);
1364  }
1365  }
1366  }
1367  } break;
1368 
1369  case 2: {
1370  // some PreSyns may get fanned out into NetCon, only
1371  // some of which have already been delivered. If this is the
1372  // case, add the q item to the fanout list. Do not put in
1373  // list if all or none have been delivered.
1374  // Actually, for simplicity, just put all the PreSyn items in
1375  // the list for fanout if PreSyn::deliver time < t.
1376  if (type == PreSynType) {
1377  if (tq->t_ < t) {
1378  tq_presyn_fanout->push_back((TQItem*) tq);
1379  }
1380  }
1381  } break;
1382  case 3: {
1383  // ??? have the ones at t been delivered or not?
1384  if (type != NetConType) {
1385  break;
1386  }
1387  if (tq->t_ == t) {
1388  DiscreteEvent* de = (DiscreteEvent*) tq->data_;
1389  de->pr("Don't know if this event has already been delivered", t, net_cvode_instance);
1390  // assert(0);
1391  }
1392  double td = tq->t_;
1393  double tt = t;
1394  // td should be on bin queue boundary
1395  if (nrn_use_bin_queue_) {
1396  // not discarded if in the current bin
1398  }
1399  if (td <= tt) {
1400  //((DiscreteEvent*)tq->data_)->pr("tq_removal_list", td,
1401  // net_cvode_instance);
1402  tq_removal_list->push_back((TQItem*) tq);
1403  }
1404  } break;
1405  }
1406 }
1408  hoc_Item* q;
1409  assert(!pp2de); // one only or make it a field.
1410  int n = nct->count;
1411  pp2de.reset(new PP2DE);
1412  pp2de->reserve(n + 1);
1413  sewrap_list = new SEWrapList();
1414  ITERATE(q, nct->olist) {
1415  NetCon* nc = (NetCon*) OBJ(q)->u.this_pointer;
1416  // ignore NetCon with no PreSyn.
1417  // i.e we do not save or restore information about
1418  // NetCon.event. We do save NetCons with local NetStim source.
1419  // But that NetStim can only be the source for one NetCon.
1420  // (because its state info will be attached to the
1421  // target synapse)
1422  if (!nc->src_) {
1423  continue;
1424  }
1425  // has a gid or else only one connection
1426  assert(nc->src_->gid_ >= 0 || nc->src_->dil_.size() == 1);
1427  Point_process* pp = nc->target_;
1428  DEList* dl = new DEList;
1429  dl->de = nc;
1430  dl->next = 0;
1431  DEList* dl1;
1432  const auto& delistiter = pp2de->find(pp);
1433  // NetCons first then SelfEvents
1434  if (delistiter != pp2de->end()) {
1435  dl1 = delistiter->second;
1436  while (dl1->next) {
1437  dl1 = dl1->next;
1438  }
1439  dl1->next = dl;
1440  } else {
1441  (*pp2de)[pp] = dl;
1442  }
1443  }
1445  callback_mode = 0;
1447 }
1448 
1449 static std::unique_ptr<Int2DblList> presyn_queue;
1450 
1451 static void del_presyn_info() {
1452  if (presyn_queue) {
1453  for (const auto& dl: *presyn_queue) {
1454  delete dl.second;
1455  }
1456  presyn_queue.reset();
1457  }
1458  if (nc2dblist) {
1459  for (const auto& dl: *nc2dblist) {
1460  delete dl.second;
1461  }
1462  nc2dblist.reset();
1463  }
1464 }
1465 
1467  DEList* dl1;
1468  if (!pp2de) {
1469  return;
1470  }
1471  for (const auto& dlpair: *pp2de) {
1472  auto dl = dlpair.second;
1473  for (; dl; dl = dl1) {
1474  dl1 = dl->next;
1475  // need to delete SEWrap items but dl->de that
1476  // are not SEWrap may already be deleted.
1477  // Hence the extra sewrap_list and items are
1478  // deleted below.
1479  delete dl;
1480  }
1481  }
1482  pp2de.reset();
1483  if (sewrap_list) {
1484  for (SEWrap* sewrap: *sewrap_list) {
1485  delete sewrap;
1486  }
1487  delete sewrap_list;
1488  sewrap_list = NULL;
1489  }
1490  del_presyn_info();
1491 }
1492 
1495  if (!ssi) {
1496  ssi_def();
1497  }
1498 }
1500  if (pp2de) {
1501  del_pp2de();
1502  }
1504 }
1506  f = io;
1507  bbss = this;
1508  core();
1509 };
1510 
1512  if (debug) {
1513  sprintf(dbuf, "Enter core()");
1514  PDEBUG;
1515  }
1516  char buf[100];
1517  sprintf(buf, "//core");
1518  f->s(buf, 1);
1519  init();
1520  gids();
1521  finish();
1522  if (debug) {
1523  sprintf(dbuf, "Leave core()");
1524  PDEBUG;
1525  }
1526 }
1527 
1529  mk_base2spgid();
1530  mk_pp2de();
1531  mk_presyn_info();
1532 }
1533 
1535  del_pp2de();
1536  del_presyn_info();
1537  base2spgid.reset();
1538  if (f->type() == BBSS_IO::IN) {
1540  }
1541 }
1542 
1543 static void base2spgid_item(int spgid, Object* obj) {
1544  int base = spgid % 10000000;
1545  if (spgid == base || !base2spgid->count(base)) {
1546  (*base2spgid)[base] = spgid;
1547  }
1548  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1549  // must be Python Cell
1550  hoc_obj_unref(obj);
1551  }
1552 }
1553 
1555  base2spgid.reset(new Int2Int());
1556  base2spgid->reserve(1000);
1558 }
1559 
1560 // c++ blue brain write interface in two phases. First return to bb what is
1561 // needed for each gid and then get from bb a series of gid, buffer
1562 // pairs to write the buffer. The read interface only requires a single
1563 // phase where we receive from bb a series of gid, buffer pairs for
1564 // reading. However a final step is required to transfer PreSyn spikes by
1565 // calling BBSaveState:finish().
1566 
1567 // first phase for writing
1568 // the caller is responsible for free(*gids) and free(*cnts)
1569 // when finished with them.
1570 int BBSaveState::counts(int** gids, int** cnts) {
1571  f = new BBSS_Cnt();
1572  BBSS_Cnt* c = (BBSS_Cnt*) f;
1573  bbss = this;
1574  init();
1575  // how many
1576  int gidcnt = base2spgid->size();
1577  if (gidcnt) {
1578  // using malloc instead of new as we might need to
1579  // deallocate from c code (of mod files)
1580  *gids = (int*) malloc(gidcnt * sizeof(int));
1581  *cnts = (int*) malloc(gidcnt * sizeof(int));
1582 
1583  if (*gids == NULL || *cnts == NULL) {
1584  printf("Error : Memory allocation failure in BBSaveState\n");
1585 #if NRNMPI
1586  nrnmpi_abort(-1);
1587 #else
1588  abort();
1589 #endif
1590  }
1591  }
1592  gidcnt = 0;
1593  for (const auto& pair: *base2spgid) {
1594  auto base = pair.first;
1595  auto spgid = pair.second;
1596  (*gids)[gidcnt] = base;
1597  c->ni = c->nd = c->ns = c->nl = 0;
1598  Object* obj = nrn_gid2obj(spgid);
1599  gidobj(spgid, obj);
1600  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1601  hoc_obj_unref(obj);
1602  }
1603  (*cnts)[gidcnt] = c->bytecnt();
1604  ++gidcnt;
1605  }
1606  delete f;
1607  return gidcnt;
1608 }
1609 
1610 // note that a cell on a processor consists of any number of
1611 // pieces of a whole cell and each piece has its own spgid
1612 // (see nrn/share/lib/hoc/binfo.hoc) The piece with the output port
1613 // has spgid == basegid with a PreSyn.output_index_ = basegid
1614 // and the others have a PreSyn.output_index_ = -2
1615 // in all cases the PreSyn.gid_ = spgid. (see netpar.cpp BBS::cell())
1616 // Note that binfo.hoc provides base_gid(spgid) which is spgid%msgid
1617 // where msgid is typically 1e7 if the maximum basegid is less than that.
1618 // thishost_gid(basegid) returns the minimum spgid
1619 // of the pieces for the cell on thishost. It would be great if we
1620 // did not have to use the value of msgid. How could we safely derive it?
1621 // In general, different models invent different mappings between basegid
1622 // and msgid so ultimately (if not restricted to Blue Brain usage)
1623 // it is necessary to supply a user defined hoc (or python) callback
1624 // to compute base_gid(spgid). Then the writer and reader can create
1625 // a map of basegid to cell and use only basegid (never reading or writing
1626 // the spgid). If there is no basegid callback then we presume there
1627 // are no split cells.
1628 
1629 // a gid item for second phase of writing
1630 void BBSaveState::gid2buffer(int gid, char* buffer, int size) {
1631  if (f) {
1632  delete f;
1633  }
1634  f = new BBSS_BufferOut(buffer, size);
1635  Object* obj = nrn_gid2obj(gid);
1636  gidobj(gid, obj);
1637  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1638  hoc_obj_unref(obj);
1639  }
1640  delete f;
1641  f = 0;
1642 }
1643 
1644 // a gid item for first phase of reading
1645 void BBSaveState::buffer2gid(int gid, char* buffer, int size) {
1646  if (f) {
1647  delete f;
1648  }
1649  f = new BBSS_BufferIn(buffer, size);
1650  Object* obj = nrn_gid2obj(gid);
1651  gidobj(gid, obj);
1652  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1653  hoc_obj_unref(obj);
1654  }
1655  delete f;
1656  f = 0;
1657 }
1658 
1659 static void cb_gidobj(int gid, Object* obj) {
1660  bbss->gidobj(gid, obj);
1661  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1662  hoc_obj_unref(obj);
1663  }
1664 }
1665 
1667  if (debug) {
1668  sprintf(dbuf, "Enter gids()");
1669  PDEBUG;
1670  }
1672  if (debug) {
1673  sprintf(dbuf, "Leave gids()");
1674  PDEBUG;
1675  }
1676 }
1677 
1678 void BBSaveState::gidobj(int basegid) {
1679  int spgid;
1680  Object* obj;
1681  const auto& spgiditer = base2spgid->find(basegid);
1682  nrn_assert(spgiditer != base2spgid->end());
1683  spgid = spgiditer->second;
1684  obj = nrn_gid2obj(spgid);
1685  gidobj(spgid, obj);
1686  if (obj && !obj->secelm_ && !is_point_process(obj)) {
1687  hoc_obj_unref(obj);
1688  }
1689 }
1690 
1691 void BBSaveState::gidobj(int gid, Object* obj) {
1692  char buf[256];
1693  int rgid = gid;
1694  if (debug) {
1695  sprintf(dbuf, "Enter gidobj(%d, %s)", gid, hoc_object_name(obj));
1696  PDEBUG;
1697  }
1698  sprintf(buf, "begin cell");
1699  f->s(buf, 1);
1700  f->i(rgid); // on reading, we promptly ignore rgid from now on, stick with gid
1701  int size = cellsize(obj);
1702  f->i(size);
1703  cell(obj);
1704  possible_presyn(gid);
1705  sprintf(buf, "end cell");
1706  f->s(buf, 1);
1707  if (debug) {
1708  sprintf(dbuf, "Leave gidobj(%d, %s)", gid, hoc_object_name(obj));
1709  PDEBUG;
1710  }
1711 }
1712 
1714  if (debug) {
1715  sprintf(dbuf, "Enter cellsize(%s)", hoc_object_name(c));
1716  PDEBUG;
1717  }
1718  int cnt = -1;
1719  if (f->type() == BBSS_IO::OUT) {
1720  BBSS_IO* sav = f;
1721  f = new BBSS_Cnt();
1722  cell(c);
1723  cnt = ((BBSS_Cnt*) f)->bytecnt();
1724  delete f;
1725  f = sav;
1726  }
1727  if (debug) {
1728  sprintf(dbuf, "Leave cellsize(%s)", hoc_object_name(c));
1729  PDEBUG;
1730  }
1731  return cnt;
1732 }
1733 
1734 // what is the section list for sections associated with a PythonObject.
1735 
1736 // Searching through the global section_list for each cell to create
1737 // a seclist for that cell has unacceptable quadratic
1738 // performance. So search once and create map from PythonCell to SecName2Sec
1739 // map. on first request. The SecName2Sec map associates the base section name'
1740 // to the Section* wrapped by a Python Section. Using a SecName2Sec map
1741 // rather than a seclist allows similar use for save and restore.
1742 
1743 typedef std::unordered_map<std::string, Section*> SecName2Sec;
1744 static std::unordered_map<void*, SecName2Sec> pycell_name2sec_maps;
1745 
1746 // clean up after a restore
1748  pycell_name2sec_maps.clear();
1749 }
1750 
1753  hoc_Item* qsec;
1754  // ForAllSections(sec)
1755  ITERATE(qsec, section_list) {
1756  Section* sec = hocSEC(qsec);
1757  if (sec->prop && sec->prop->dparam[PROP_PY_INDEX]._pvoid) { // PythonSection
1758  // Assume we can associate with a Python Cell
1759  // Sadly, cannot use nrn_sec2cell Object* as the key because it
1760  // is not unique and the map needs definite PyObject* keys.
1761  Object* ho = nrn_sec2cell(sec);
1762  if (ho) {
1763  void* pycell = nrn_opaque_obj2pyobj(ho);
1764  hoc_obj_unref(ho);
1765  if (pycell) {
1766  SecName2Sec& sn2s = pycell_name2sec_maps[pycell];
1767  std::string name = secname(sec);
1768  // basename is after the cell name component that ends in '.'.
1769  size_t last_dot = name.rfind(".");
1770  assert(last_dot != std::string::npos);
1771  assert(name.size() > (last_dot + 1));
1772  std::string basename = name.substr(last_dot + 1);
1773  if (sn2s.find(basename) != sn2s.end()) {
1774  hoc_execerr_ext("Python Section name, %s, is not unique in the Python cell",
1775  name.c_str());
1776  }
1777  sn2s[basename] = sec;
1778  continue;
1779  }
1780  }
1781  hoc_execerr_ext("Python Section, %s, not associated with Python Cell.", secname(sec));
1782  }
1783  }
1784 }
1785 
1787  if (pycell_name2sec_maps.empty()) {
1789  }
1790  void* pycell = nrn_opaque_obj2pyobj(c);
1791  auto search = pycell_name2sec_maps.find(pycell);
1792  assert(search != pycell_name2sec_maps.end());
1793  return search->second;
1794 }
1795 
1796 // Here is the major place where there is a symmetry break between writing
1797 // and reading. That is because of the possibility of splitting where
1798 // not only the pieces are distributed differently between saving and restoring
1799 // processors but the pieces themselves (as well as the piece gids)
1800 // are different. It is only the union of pieces that describes a complete cell.
1801 // Symmetry exists again at the section level
1802 // For writing the cell is defined as the existing set of pieces in the hoc
1803 // cell Object. Here in cell we write enough prefix info about the Section
1804 // to be able to determine if it exists before reading with section(sec);
1805 
1807  if (debug) {
1808  sprintf(dbuf, "Enter cell(%s)", hoc_object_name(c));
1809  PDEBUG;
1810  }
1811  char buf[256];
1812  sprintf(buf, "%s", hoc_object_name(c));
1813  f->s(buf);
1814  if (!is_point_process(c)) { // must be cell object
1815  if (f->type() != BBSS_IO::IN) { // writing, counting
1816  // from forall_section in cabcode.cpp
1817  // count, and iterate from first to last
1818  hoc_Item *qsec, *first, *last;
1819  int cnt = 0;
1820  Section* sec;
1821  qsec = c->secelm_;
1822  if (qsec) { // Write HOC Cell
1823  for (first = qsec; first->itemtype && hocSEC(first)->prop->dparam[6].obj == c;
1824  first = first->prev) {
1825  sec = hocSEC(first);
1826  if (sec->prop) {
1827  ++cnt;
1828  }
1829  }
1830  first = first->next;
1831  last = qsec->next;
1832  f->i(cnt);
1833  for (qsec = first; qsec != last; qsec = qsec->next) {
1834  Section* sec = hocSEC(qsec);
1835  if (sec->prop) {
1836  // the section exists
1837  sprintf(buf, "begin section");
1838  f->s(buf);
1840  section(sec);
1841  sprintf(buf, "end section");
1842  f->s(buf);
1843  }
1844  }
1845  } else { // Write Python Cell
1846  // secelm_ is NULL if c is a PythonObject. Need to get
1847  // the list of sections associated with c in some other way.
1849  int i = (int) (n2s.size());
1850  f->i(i);
1851  for (auto& iter: n2s) {
1852  const std::string& name = iter.first;
1853  Section* sec = iter.second;
1854  assert(sec->prop); // all exist because n2s derived from global
1855  // section_list.
1856  sprintf(buf, "begin section");
1857  f->s(buf);
1858  strcpy(buf, name.c_str());
1859  f->s(buf);
1860  int indx = sec->prop->dparam[5].i;
1861  f->i(indx);
1862  int size = sectionsize(sec);
1863  f->i(size, 1);
1864  section(sec);
1865  sprintf(buf, "end section");
1866  f->s(buf);
1867  }
1868  }
1869  } else { // reading
1870  Section* sec;
1871  int i, cnt, indx, size;
1872 
1873  // Don't know a better idiom for following.
1874  SecName2Sec* n2s = NULL;
1875  if (!c->secelm_) {
1876  n2s = &pycell_name2sec_map(c);
1877  }
1878  // Assert that all section name are unique for a Python Cell
1879  std::unordered_set<std::string> snames;
1880 
1881  f->i(cnt);
1882  for (i = 0; i < cnt; ++i) {
1883  sprintf(buf, "begin section");
1884  f->s(buf, 1);
1885  f->s(buf); // the section name
1886  f->i(indx); // section array index
1887  f->i(size);
1888  if (c->secelm_) { // HOC cell
1890  } else {
1891  sec = NULL;
1892  if (snames.find(buf) != snames.end()) {
1893  hoc_execerr_ext("More than one section with name %s in cell %s",
1894  buf,
1895  hoc_object_name(c));
1896  }
1897  snames.emplace(buf);
1898  auto search = (*n2s).find(buf);
1899  if (search != (*n2s).end()) {
1900  sec = search->second;
1901  }
1902  }
1903  if (sec) {
1904  section(sec);
1905  } else { // skip size bytes
1906  f->skip(size);
1907  }
1908  sprintf(buf, "end section");
1909  f->s(buf, 1);
1910  }
1911  }
1912  } else { // ARTIFICIAL_CELL
1913  Point_process* pnt = ob2pntproc(c);
1914  mech(pnt->prop);
1915  }
1916  if (debug) {
1917  sprintf(dbuf, "Leave cell(%s)", hoc_object_name(c));
1918  PDEBUG;
1919  }
1920 }
1921 
1923  char buf[256];
1924  // not used for python sections
1925  assert(!sec->prop->dparam[PROP_PY_INDEX]._pvoid);
1926  Symbol* sym = sec->prop->dparam[0].sym;
1927  if (sym) {
1928  sprintf(buf, "%s", sym->name);
1929  f->s(buf);
1930  }
1931  int indx = sec->prop->dparam[5].i;
1932  f->i(indx);
1933  int size = sectionsize(sec);
1934  f->i(size, 1);
1935 }
1936 
1938  if (debug) {
1939  sprintf(dbuf, "Enter section(%s)", sec->prop->dparam[0].sym->name);
1940  PDEBUG;
1941  }
1942  seccontents(sec);
1943  if (debug) {
1944  sprintf(dbuf, "Leave section(%s)", sec->prop->dparam[0].sym->name);
1945  PDEBUG;
1946  }
1947 }
1948 
1950  if (debug == 1) {
1951  sprintf(dbuf, "Enter sectionsize(%s)", sec->prop->dparam[0].sym->name);
1952  PDEBUG;
1953  }
1954  // should be same for both IN and OUT
1955  int cnt = -1;
1956  if (f->type() != BBSS_IO::CNT) {
1957  BBSS_IO* sav = f;
1958  f = new BBSS_Cnt();
1959  seccontents(sec);
1960  cnt = ((BBSS_Cnt*) f)->bytecnt();
1961  delete f;
1962  f = sav;
1963  }
1964  if (debug == 1) {
1965  sprintf(dbuf, "Leave sectionsize(%s)", sec->prop->dparam[0].sym->name);
1966  PDEBUG;
1967  }
1968  return cnt;
1969 }
1970 
1972  if (debug) {
1973  sprintf(dbuf, "Enter seccontents(%s)", sec->prop->dparam[0].sym->name);
1974  PDEBUG;
1975  }
1976  int i, nseg;
1977  char buf[100];
1978  sprintf(buf, "//contents");
1979  f->s(buf);
1980  nseg = sec->nnode - 1;
1981  f->i(nseg, 1);
1982  for (i = 0; i < nseg; ++i) {
1983  node(sec->pnode[i]);
1984  }
1985  node01(sec, sec->parentnode);
1986  node01(sec, sec->pnode[nseg]);
1987  if (debug) {
1988  sprintf(dbuf, "Leave seccontents(%s)", sec->prop->dparam[0].sym->name);
1989  PDEBUG;
1990  }
1991 }
1992 
1993 // all Point_process and mechanisms -- except IGNORE point process instances
1995  if (debug) {
1996  sprintf(dbuf, "Enter node(nd)");
1997  PDEBUG;
1998  }
1999  int i;
2000  Prop* p;
2001  f->d(1, NODEV(nd));
2002  // count
2003  // On restore, new point processes may have been inserted in
2004  // the section and marked IGNORE. So we need to count only the
2005  // non-ignored.
2006  for (i = 0, p = nd->prop; p; p = p->next) {
2007  if (p->type > 3) {
2008  if (memb_func[p->type].is_point) {
2009  if (!ignored(p)) {
2010  ++i;
2011  }
2012  } else { // density mechanism
2013  ++i;
2014  }
2015  }
2016  }
2017  f->i(i, 1);
2018  for (p = nd->prop; p; p = p->next) {
2019  if (p->type > 3) {
2020  mech(p);
2021  }
2022  }
2023  if (debug) {
2024  sprintf(dbuf, "Leave node(nd)");
2025  PDEBUG;
2026  }
2027 }
2028 
2029 // only Point_process that belong to Section
2031  if (debug) {
2032  sprintf(dbuf, "Enter node01(sec, nd)");
2033  PDEBUG;
2034  }
2035  int i;
2036  Prop* p;
2037  // It is not clear why the zero area node voltages need to be saved.
2038  // Without them, we get correct simulations after a restore for
2039  // whole cells but not for split cells.
2040  f->d(1, NODEV(nd));
2041  // count
2042  for (i = 0, p = nd->prop; p; p = p->next) {
2043  if (memb_func[p->type].is_point) {
2044  Point_process* pp = (Point_process*) p->dparam[1]._pvoid;
2045  if (pp->sec == sec) {
2046  if (!ignored(p)) {
2047  ++i;
2048  }
2049  }
2050  }
2051  }
2052  f->i(i, 1);
2053  for (p = nd->prop; p; p = p->next) {
2054  if (memb_func[p->type].is_point) {
2055  Point_process* pp = (Point_process*) p->dparam[1]._pvoid;
2056  if (pp->sec == sec) {
2057  mech(p);
2058  }
2059  }
2060  }
2061  if (debug) {
2062  sprintf(dbuf, "Leave node01(sec, nd)");
2063  PDEBUG;
2064  }
2065 }
2066 
2068  if (debug) {
2069  sprintf(dbuf, "Enter mech(prop type %d)", p->type);
2070  PDEBUG;
2071  }
2072  int type = p->type;
2073  if (memb_func[type].is_point && ignored(p)) {
2074  return;
2075  }
2076  f->i(type, 1);
2077  char buf[100];
2078  sprintf(buf, "//%s", memb_func[type].sym->name);
2079  f->s(buf, 1);
2080  f->d(ssi[p->type].size, p->param + ssi[p->type].offset);
2081  Point_process* pp = 0;
2082  if (memb_func[p->type].is_point) {
2083  pp = (Point_process*) p->dparam[1]._pvoid;
2084  if (pnt_receive[p->type]) {
2085  // associated NetCon and queue SelfEvent
2086  // if the NetCon has a unique non-gid source (art cell)
2087  // that source is save/restored as well.
2088  netrecv_pp(pp);
2089  }
2090  }
2091  if (ssi[p->type].callback) { // model author dependent info
2092  // the POINT_PROCESS or SUFFIX has a bbsavestate function
2093  sprintf(buf, "callback");
2094  f->s(buf, 1);
2095  int narg = 2;
2096  double xdir = -1.0; // -1 size, 0 save, 1 restore
2097  double* xval = NULL; // returned for save, sent for restore.
2098 
2099  hoc_pushpx(&xdir);
2100  hoc_pushpx(xval);
2101  if (memb_func[p->type].is_point) {
2102  hoc_call_ob_proc(pp->ob, ssi[p->type].callback, narg);
2103  hoc_xpop();
2104  } else {
2105  nrn_call_mech_func(ssi[p->type].callback, narg, p, p->type);
2106  }
2107  int sz = int(xdir);
2108  if (sz > 0) {
2109  xval = new double[sz];
2110 
2111  hoc_pushpx(&xdir);
2112  hoc_pushpx(xval);
2113  if (f->type() == BBSS_IO::IN) { // restore
2114  xdir = 1.;
2115  f->d(sz, xval);
2116  if (memb_func[p->type].is_point) {
2117  hoc_call_ob_proc(pp->ob, ssi[p->type].callback, narg);
2118  hoc_xpop();
2119  } else {
2120  nrn_call_mech_func(ssi[p->type].callback, narg, p, p->type);
2121  }
2122  } else {
2123  xdir = 0.;
2124  if (memb_func[p->type].is_point) {
2125  hoc_call_ob_proc(pp->ob, ssi[p->type].callback, narg);
2126  hoc_xpop();
2127  } else {
2128  nrn_call_mech_func(ssi[p->type].callback, narg, p, p->type);
2129  }
2130  f->d(sz, xval);
2131  }
2132  delete[] xval;
2133  }
2134  }
2135  if (debug) {
2136  sprintf(dbuf, "Leave mech(prop type %d)", p->type);
2137  PDEBUG;
2138  }
2139 }
2140 
2142  if (debug) {
2143  sprintf(dbuf, "Enter netrecv_pp(pp prop type %d)", pp->prop->type);
2144  PDEBUG;
2145  }
2146  char buf[1000];
2147  sprintf(buf, "%s", hoc_object_name(pp->ob));
2148  f->s(buf);
2149 
2150  // associated NetCon, and queue SelfEvent
2151  DEList *dl, *dl1;
2152  const auto& dliter = pp2de->find(pp);
2153  if (dliter == pp2de->end()) {
2154  dl = 0;
2155  } else {
2156  dl = dliter->second;
2157  }
2158  int cnt = 0;
2159  // dl has the NetCons first then the SelfEvents
2160  // NetCon
2161  for (cnt = 0, dl1 = dl; dl1 && dl1->de->type() == NetConType; dl1 = dl1->next) {
2162  ++cnt;
2163  }
2164  f->i(cnt, 1);
2165  sprintf(buf, "NetCon");
2166  f->s(buf, 1);
2167  for (; dl && dl->de->type() == NetConType; dl = dl->next) {
2168  NetCon* nc = (NetCon*) dl->de;
2169  f->d(nc->cnt_, nc->weight_);
2170  if (f->type() != BBSS_IO::IN) { // writing, counting
2171  DblList* db = 0;
2172  int j = 0;
2173  if (nc2dblist) {
2174  const auto& dbiter = nc2dblist->find(nc);
2175  if (dbiter != nc2dblist->end()) {
2176  db = dbiter->second;
2177  j = db->size();
2178  f->i(j);
2179  for (int i = 0; i < j; ++i) {
2180  double x = (*db)[i];
2181  f->d(1, x);
2182  }
2183  } else {
2184  f->i(j);
2185  }
2186  } else {
2187  f->i(j);
2188  }
2189  int has_stim = 0;
2190  if (nc->src_ && nc->src_->osrc_ && nc->src_->gid_ < 0) {
2191  // save the associated local stimulus
2192  has_stim = 1;
2193  f->i(has_stim, 1);
2194  Point_process* pp = ob2pntproc(nc->src_->osrc_);
2195  mech(pp->prop);
2196  } else {
2197  f->i(has_stim, 1);
2198  }
2199  } else { // reading
2200  int j = 0;
2201  f->i(j);
2202  for (int i = 0; i < j; ++i) {
2203  double x;
2204  f->d(1, x);
2205  x = binq_time(x);
2206  nrn_netcon_event(nc, x);
2207  }
2208  int has_stim = 0;
2209  f->i(has_stim);
2210  if (has_stim) {
2211  assert((nc->src_ && nc->src_->osrc_ && nc->src_->gid_ < 0) == has_stim);
2212  Point_process* pp = ob2pntproc(nc->src_->osrc_);
2213  mech(pp->prop);
2214  }
2215  }
2216  }
2217  // Count SelfEvents. At this point, for restore, there are no
2218  // SelfEvents (so cnt is 0) because the queue has been cleared.
2219  for (cnt = 0, dl1 = dl; dl1 && dl1->de->type() == SelfEventType; dl1 = dl1->next) {
2220  ++cnt;
2221  }
2222  f->i(cnt);
2223  // SelfEvent existence depends on state. For reading the queue
2224  // is empty. So count and save is different from restore.
2225 
2226  if (f->type() != BBSS_IO::IN) {
2227  for (; dl && dl->de->type() == SelfEventType; dl = dl->next) {
2228  SEWrap* sew = (SEWrap*) dl->de;
2229  sprintf(buf, "SelfEvent");
2230  f->s(buf);
2231  f->d(1, sew->se->flag_);
2232  f->d(1, sew->tt);
2233  f->i(sew->ncindex);
2234  int moff = -1;
2235  if (sew->se->movable_) {
2236  moff = (Datum*) (sew->se->movable_) - pp->prop->dparam;
2237  }
2238  f->i(moff);
2239  }
2240  } else { // restore
2241  // For restore it is necessary to create the proper SelfEvents.
2242  // since the queue has been cleared.
2243  for (int i = 0; i < cnt; ++i) {
2244  int ncindex, moff;
2245  double flag, tt, *w;
2246  f->s(buf);
2247  f->d(1, flag);
2248  f->d(1, tt);
2249  f->i(ncindex);
2250  f->i(moff);
2251  void** movable = NULL;
2252  TQItem* tqi;
2253  // new SelfEvent item mostly filled in.
2254  // But starting out with NULL weight vector and
2255  // flag=1 so that tqi->data is the new SelfEvent
2256  net_send((void**) &tqi, NULL, pp, tt, 1.0);
2257  assert(tqi && tqi->data_ && ((DiscreteEvent*) tqi->data_)->type() == SelfEventType);
2258  SelfEvent* se = (SelfEvent*) tqi->data_;
2259  se->flag_ = flag;
2260  if (moff >= 0) {
2261  movable = &(pp->prop->dparam[moff]._pvoid);
2262  if (flag == 1) {
2263  *movable = tqi;
2264  }
2265  se->movable_ = movable;
2266  }
2267  if (ncindex == -1) {
2268  w = NULL;
2269  } else {
2270  int j;
2271  for (j = 0, dl1 = dliter->second; j < ncindex; ++j, dl1 = dl1->next) {
2272  ;
2273  }
2274  assert(dl1 && dl1->de->type() == NetConType);
2275  w = ((NetCon*) dl1->de)->weight_;
2276  }
2277  se->weight_ = w;
2278  }
2279  }
2280  if (debug) {
2281  sprintf(dbuf, "Leave netrecv_pp(pp prop type %d)", pp->prop->type);
2282  PDEBUG;
2283  }
2284 }
2285 
2286 static int giddest_size;
2287 static int* giddest;
2288 static int* tsdest_cnts;
2289 static double* tsdest;
2290 
2291 // input scnt, sdipl ; output rcnt, rdispl
2292 static void all2allv_helper(int* scnt, int* sdispl, int* rcnt, int* rdispl) {
2293  int i;
2294  int np = nrnmpi_numprocs;
2295  int* c = new int[np];
2296  rdispl[0] = 0;
2297  for (i = 0; i < np; ++i) {
2298  c[i] = 1;
2299  rdispl[i + 1] = rdispl[i] + c[i];
2300  }
2301  nrnmpi_int_alltoallv(scnt, c, rdispl, rcnt, c, rdispl);
2302  delete[] c;
2303  rdispl[0] = 0;
2304  for (i = 0; i < np; ++i) {
2305  rdispl[i + 1] = rdispl[i] + rcnt[i];
2306  }
2307 }
2308 
2309 static void all2allv_int2(int* scnt, int* sdispl, int* gidsrc, int* ndsrc) {
2310 #if NRNMPI
2311  int np = nrnmpi_numprocs;
2312  int* rcnt = new int[np];
2313  int* rdispl = new int[np + 1];
2314  all2allv_helper(scnt, sdispl, rcnt, rdispl);
2315 
2316  giddest = 0;
2317  tsdest_cnts = 0;
2318  giddest_size = rdispl[np];
2319  if (giddest_size) {
2320  giddest = new int[giddest_size];
2321  tsdest_cnts = new int[giddest_size];
2322  }
2323  nrnmpi_int_alltoallv(gidsrc, scnt, sdispl, giddest, rcnt, rdispl);
2324  nrnmpi_int_alltoallv(ndsrc, scnt, sdispl, tsdest_cnts, rcnt, rdispl);
2325 
2326  delete[] rcnt;
2327  delete[] rdispl;
2328 #else
2329  assert(0);
2330 #endif
2331 }
2332 
2333 static void all2allv_dbl1(int* scnt, int* sdispl, double* tssrc) {
2334 #if NRNMPI
2335  int size;
2336  int np = nrnmpi_numprocs;
2337  int* rcnt = new int[np];
2338  int* rdispl = new int[np + 1];
2339  all2allv_helper(scnt, sdispl, rcnt, rdispl);
2340 
2341  tsdest = 0;
2342  size = rdispl[np];
2343  if (size) {
2344  tsdest = new double[size];
2345  }
2346  nrnmpi_dbl_alltoallv(tssrc, scnt, sdispl, tsdest, rcnt, rdispl);
2347 
2348  delete[] rcnt;
2349  delete[] rdispl;
2350 #else
2351  assert(0);
2352 #endif
2353 }
2354 
2355 static void scatteritems() {
2356  // since gid queue items with the same gid are scattered on
2357  // many processors, we send all the gid,tscnt pairs (and tslists
2358  // with undelivered NetCon counts)
2359  // to the round-robin host (we do not know the gid owner host yet).
2360  int i, host;
2361  src2send_cnt = 0;
2362  src2send.reset(new Int2DblList());
2363  src2send->reserve(1000);
2365  // if event on queue at t we will not be able to decide whether or
2366  // not it should be delivered during restore.
2367  // The assert was moved into mk_presyn_info since this function is
2368  // reused by bbss_queuecheck and the error in that context will
2369  // be analyzed there.
2370  // assert(tq->least_t() > nrn_threads->_t);
2371  callback_mode = 1;
2373  // space inefficient but simple support analogous to pc.all2all
2374  int* gidsrc = 0;
2375  int* ndsrc = 0; // count for each DblList, parallel to gidsrc
2376  double* tssrc = 0; // all the doubles (and undelivered NetCon counts) in every DblList
2377  if (src2send_cnt) {
2378  int ndsrctotal = 0;
2379  gidsrc = new int[src2send_cnt];
2380  ndsrc = new int[src2send_cnt];
2381  for (const auto& pair: *src2send) {
2382  ndsrctotal += pair.second->size();
2383  }
2384  tssrc = new double[ndsrctotal];
2385  }
2386  int* off = new int[nrnmpi_numprocs + 1]; // offsets for gidsrc and ndsrc
2387  int* cnts = new int[nrnmpi_numprocs]; // dest host counts for gidsrc and ndsrc
2388  int* doff = new int[nrnmpi_numprocs + 1]; // offsets for doubles
2389  int* dcnts = new int[nrnmpi_numprocs + 1]; // dest counts of the doubles
2390  for (i = 0; i < nrnmpi_numprocs; ++i) {
2391  cnts[i] = 0;
2392  dcnts[i] = 0;
2393  }
2394  // counts to send to each destination rank
2395  for (const auto& pair: *src2send) {
2396  int gid = pair.first;
2397  int host = gid % nrnmpi_numprocs;
2398  ++cnts[host];
2399  dcnts[host] += pair.second->size();
2400  }
2401  // offsets
2402  off[0] = 0;
2403  doff[0] = 0;
2404  for (i = 0; i < nrnmpi_numprocs; ++i) {
2405  off[i + 1] = off[i] + cnts[i];
2406  doff[i + 1] = doff[i] + dcnts[i];
2407  }
2408  // what to send to each destination. Note dcnts and ndsrc are NOT the same.
2409  for (i = 0; i < nrnmpi_numprocs; ++i) {
2410  cnts[i] = 0;
2411  dcnts[i] = 0;
2412  }
2413  for (const auto& pair: *src2send) {
2414  const auto dl = pair.second;
2415  int gid = pair.first;
2416  host = gid % nrnmpi_numprocs;
2417  gidsrc[off[host] + cnts[host]] = gid;
2418  ndsrc[off[host] + cnts[host]++] = int(dl->size());
2419  for (size_t i = 0; i < dl->size(); ++i) {
2420  tssrc[doff[host] + dcnts[host]++] = (*dl)[i];
2421  }
2422  }
2423  for (const auto& pair: *src2send) {
2424  delete pair.second;
2425  }
2426  src2send.reset();
2427 
2428  if (nrnmpi_numprocs > 1) {
2429  all2allv_int2(cnts, off, gidsrc, ndsrc);
2430  all2allv_dbl1(dcnts, doff, tssrc);
2431  if (gidsrc) {
2432  delete[] gidsrc;
2433  delete[] ndsrc;
2434  delete[] tssrc;
2435  }
2436  } else {
2437  giddest_size = cnts[0];
2438  giddest = gidsrc;
2439  tsdest_cnts = ndsrc;
2440  tsdest = tssrc;
2441  }
2442  delete[] cnts;
2443  delete[] off;
2444  delete[] dcnts;
2445  delete[] doff;
2446 }
2447 
2448 static void allgatherv_helper(int cnt, int* rcnt, int* rdspl) {
2449  nrnmpi_int_allgather(&cnt, rcnt, 1);
2450  rdspl[0] = 0;
2451  for (int i = 0; i < nrnmpi_numprocs; ++i) {
2452  rdspl[i + 1] = rdspl[i] + rcnt[i];
2453  }
2454 }
2455 
2456 static void spikes_on_correct_host(int cnt,
2457  int* g,
2458  int* dcnts,
2459  int tscnt,
2460  double* ts,
2461  Int2DblList* m) {
2462  // tscnt is the sum of the dcnts.
2463  // i.e. the send times and undelivered NetCon counts.
2464  int i, rsize, *rg = NULL, *rtscnts = NULL;
2465  double* rts = NULL;
2466  // not at all space efficient
2467  if (nrnmpi_numprocs > 1) {
2468  int* cnts = new int[nrnmpi_numprocs];
2469  int* dspl = new int[nrnmpi_numprocs + 1];
2470  allgatherv_helper(cnt, cnts, dspl);
2471  rsize = dspl[nrnmpi_numprocs];
2472  if (rsize) {
2473  rg = new int[rsize];
2474  rtscnts = new int[rsize];
2475  }
2476  nrnmpi_int_allgatherv(g, rg, cnts, dspl);
2477  nrnmpi_int_allgatherv(dcnts, rtscnts, cnts, dspl);
2478 
2479  allgatherv_helper(tscnt, cnts, dspl);
2480  rts = new double[dspl[nrnmpi_numprocs]];
2481  nrnmpi_dbl_allgatherv(ts, rts, cnts, dspl);
2482 
2483  delete[] cnts;
2484  delete[] dspl;
2485  } else {
2486  rsize = cnt;
2487  rg = g;
2488  rtscnts = dcnts;
2489  rts = ts;
2490  }
2491  int tsoff = 0;
2492  for (i = 0; i < rsize; ++i) {
2493  if (nrn_gid_exists(rg[i])) {
2494  DblList* dl = new DblList();
2495  dl->reserve(rtscnts[i]);
2496  (*m)[rg[i]] = dl;
2497  for (int j = 0; j < rtscnts[i]; ++j) {
2498  dl->push_back(rts[tsoff + j]);
2499  }
2500  }
2501  tsoff += rtscnts[i];
2502  }
2503  if (nrnmpi_numprocs > 1) {
2504  if (rg)
2505  delete[] rg;
2506  if (rtscnts)
2507  delete[] rtscnts;
2508  if (rts)
2509  delete[] rts;
2510  }
2511 }
2512 
2513 static void construct_presyn_queue() {
2514  // almost all the old mk_presyn_info factored into here since
2515  // a presyn_queue is optionally needed for check_queue()
2516 
2517  // note that undelivered netcon count is interleaved with tsend
2518  // in the DblList of the Int2DblList
2519  int tscnt, i;
2520  DblList* dl;
2521  if (presyn_queue) {
2522  del_presyn_info();
2523  }
2524  nc2dblist.reset(new NetCon2DblList());
2525  nc2dblist->reserve(20);
2526  scatteritems();
2527  int cnt = giddest_size;
2528  std::unique_ptr<Int2DblList> m{new Int2DblList()};
2529  m->reserve(cnt + 1);
2530  int mcnt = 0;
2531  int mdcnt = 0;
2532  int its = 0;
2533  for (i = 0; i < cnt; ++i) {
2534  int gid = giddest[i];
2535  tscnt = tsdest_cnts[i];
2536  const auto& dliter = m->find(gid);
2537  if (dliter != m->end()) {
2538  dl = dliter->second;
2539  } else {
2540  dl = new DblList();
2541  (*m)[gid] = dl;
2542  ++mcnt;
2543  }
2544  // add the initiation time(s) if they are unique ( > .1)
2545  // and also accumulate the undelivered netcon counts
2546  // otherwise assert if not identical ( > 1e-12)
2547  for (int k = 0; k < tscnt; k += 2) {
2548  double t1 = tsdest[its++];
2549  int inccnt = tsdest[its++];
2550  int add = 1;
2551  // can't hurt to test the ones just added
2552  // no thought given to efficiency
2553  // Actually, need to test to accumulate
2554  for (int j = 0; j < dl->size(); j += 2) {
2555  double dt = fabs((*dl)[j] - t1);
2556  if (dt < 1e-12) {
2557  (*dl)[j + 1] += inccnt;
2558  add = 0;
2559  break;
2560  } else if (dt < .1) {
2561  assert(0);
2562  }
2563  }
2564  if (add) {
2565  dl->push_back(t1);
2566  dl->push_back(inccnt);
2567  mdcnt += 2;
2568  }
2569  }
2570  }
2571  if (giddest) {
2572  delete[] giddest;
2573  delete[] tsdest_cnts;
2574  delete[] tsdest;
2575  }
2576  // each host now has a portion of the (gid, ts) spike pairs
2577  // (Actually (gid, list of (ts, undelivered NetCon count)) map.)
2578  // but the pairs are not on the hosts that own the gid.
2579  // The major thing that is accomplished so far is that the
2580  // up to thousands of Netcon events on the queues of thousands
2581  // of host are now a single PreSyn event on a single host.
2582  // We could save them all in separate area and read them all
2583  // and send only when a host owns the gid, but we wish
2584  // to keep the spike sent info with the cell info for the gid.
2585  // We next need to do something analogous to the allgather send
2586  // so each host can examine every spike and pick out the ones
2587  // which were sent from that host. That becomes the true
2588  // presyn_queue map.
2589  int* gidsrc = 0;
2590  int* tssrc_cnt = 0;
2591  double* tssrc = 0;
2592  if (mcnt) {
2593  gidsrc = new int[mcnt];
2594  tssrc_cnt = new int[mcnt];
2595  tssrc = new double[mdcnt];
2596  }
2597  mcnt = 0;
2598  mdcnt = 0;
2599  for (const auto& pair: *m) {
2600  auto dl = pair.second;
2601  gidsrc[mcnt] = pair.first;
2602  tssrc_cnt[mcnt] = dl->size();
2603  for (int i = 0; i < dl->size(); ++i) {
2604  tssrc[mdcnt++] = (*dl)[i];
2605  }
2606  ++mcnt;
2607  delete dl;
2608  }
2609  presyn_queue.reset(new Int2DblList());
2610  presyn_queue->reserve(127);
2611  spikes_on_correct_host(mcnt, gidsrc, tssrc_cnt, mdcnt, tssrc, presyn_queue.get());
2612  if (gidsrc) {
2613  delete[] gidsrc;
2614  delete[] tssrc_cnt;
2615  delete[] tssrc;
2616  }
2617 }
2618 
2619 void BBSaveState::mk_presyn_info() { // also the NetCon* to tdelivery map
2620  if (f->type() != BBSS_IO::IN) { // only when saving or counting
2621  // if event on queue at t we will not be able to decide
2622  // whether or not it should be delivered during restore.
2624 
2625  TQItem* tqi = tq->least();
2626  int dtype = tqi ? ((DiscreteEvent*) (tqi->data_))->type() : 0;
2627  assert(tq->least_t() > nrn_threads->_t || dtype == NetParEventType);
2629  }
2630 }
2631 
2633  if (debug) {
2634  sprintf(dbuf, "Enter possible_presyn()");
2635  PDEBUG;
2636  }
2637  char buf[100];
2638  int i;
2639  double ts;
2640  // first the presyn state associated with this gid
2641  if (nrn_gid_exists(gid) < 2) {
2642  if (f->type() == BBSS_IO::IN) {
2643  // if reading then skip what was written
2644  i = 0;
2645  f->i(i);
2646  if (i == 1) { // skip state
2647  int j;
2648  double x;
2649  sprintf(buf, "PreSyn");
2650  f->s(buf, 1);
2651  f->i(j);
2652  f->d(1, x);
2653  }
2654  } else {
2655  i = -1;
2656  f->i(i);
2657  }
2658  } else {
2659  PreSyn* ps = nrn_gid2presyn(gid);
2660  i = (ps->ssrc_ != 0 ? 1 : -1); // does it have state
2661  f->i(i, 1);
2662  int output_index = ps->output_index_;
2663  f->i(output_index);
2664  if (output_index >= 0 && i == 1) {
2665  sprintf(buf, "PreSyn");
2666  f->s(buf, 1);
2667  int j = (ps->flag_ ? 1 : 0);
2668  double th = ps->valthresh_;
2669  f->i(j);
2670  f->d(1, th);
2671  if (ps->output_index_ >= 0) {
2672  ps->flag_ = j;
2673  ps->valthresh_ = th;
2674  }
2675 #if 0
2676  f->d(1, ps->valold_);
2677  f->d(1, ps->told_);
2678  // ps->qthresh_ handling unimplemented but would not be needed
2679  // unless cvode.condition_order(2) when cvode active.
2680 #endif
2681  }
2682  }
2683  // next the possibility that a spike derived from this presyn
2684  // is on the queue.
2685  if (f->type() != BBSS_IO::IN) { // save or count
2686  DblList* dl;
2687  const auto& dliter = presyn_queue->find(gid);
2688  if (dliter != presyn_queue->end()) {
2689  dl = dliter->second;
2690  f->i(gid);
2691  i = dl->size(); // ts and undelivered netcon counts
2692  f->i(i);
2693  for (i = 0; i < dl->size(); i += 2) {
2694  ts = (*dl)[i];
2695  f->d(1, ts);
2696  int unc = (*dl)[i + 1];
2697  f->i(unc);
2698  }
2699  } else {
2700  i = -1;
2701  f->i(i);
2702  }
2703  } else { // restore
2704  f->i(i); // gid
2705  if (i >= 0) {
2706  int cnt, unc;
2707  // assert (gid == i);
2708  if (gid == i) {
2709  f->i(cnt); // both the ts and undelivered netcon counts
2710 
2711  // while re-issuing the PreSyn send, we do not want
2712  // any spike recording to take place. So, what are
2713  // the current Vector sizes, if used, so we can put
2714  // them back. This presumes we are recording only
2715  // from PreSyn with an output_gid.
2716 #if 1 // if set to 0 comment out asserts below
2717  // The only reason we save here is to allow a
2718  // assertion test when restoring the original
2719  // record vector size.
2720  int sz1 = -1;
2721  int sz2 = -1;
2722  PreSyn* ps = nrn_gid2presyn(gid);
2723  if (ps->tvec_) {
2724  sz1 = ps->tvec_->size();
2725  }
2726  if (ps->idvec_) {
2727  sz2 = ps->idvec_->size();
2728  }
2729 #endif
2730 
2731 #if QUEUECHECK
2732  // map from gid to unc for later checking
2733  if (!queuecheck_gid2unc) {
2734  queuecheck_gid2unc.reset(new Int2DblList());
2735  queuecheck_gid2unc->reserve(1000);
2736  }
2737  DblList* dl = new DblList();
2738  (*queuecheck_gid2unc)[i] = dl;
2739 #endif
2740  for (int j = 0; j < cnt; j += 2) {
2741  f->d(1, ts);
2742  f->i(unc); // expected undelivered NetCon count
2743  nrn_fake_fire(gid, ts, 2); // only owned by this
2744 #if QUEUECHECK
2745  dl->push_back(ts);
2746  dl->push_back(unc);
2747 #endif
2748  }
2749 
2750  // restore spike record sizes.
2751  if (ps->tvec_) {
2752  int sz = ps->tvec_->size() - cnt / 2;
2753  assert(sz == sz1);
2754  ps->tvec_->resize(sz);
2755  }
2756  if (ps->idvec_) {
2757  int sz = ps->idvec_->size() - cnt / 2;
2758  assert(sz == sz2);
2759  ps->idvec_->resize(sz);
2760  }
2761  } else {
2762  // skip -> file has undelivered spikes, but this is not the cell that
2763  // deals with them
2764  f->i(cnt);
2765  for (int j = 0; j < cnt; j += 2) {
2766  f->d(1, ts);
2767  f->i(unc);
2768  }
2769  }
2770  }
2771  }
2772  if (debug) {
2773  sprintf(dbuf, "Leave possible_presyn()");
2774  PDEBUG;
2775  }
2776 }
2777 
2778 #if QUEUECHECK
2779 static void bbss_queuecheck() {
2781  if (queuecheck_gid2unc)
2782  for (const auto& pair: *queuecheck_gid2unc) {
2783  auto gid = pair.first;
2784  auto dl = pair.second;
2785  DblList* dl2;
2786  const auto& dl2iter = presyn_queue->find(gid);
2787  if (dl2iter != presyn_queue->end()) {
2788  dl2 = dl2iter->second;
2789  if (dl->size() == dl2->size()) {
2790  for (int i = 0; i < dl->size(); i += 2) {
2791  if ((fabs((*dl)[i] - (*dl2)[i]) > 1e-12) || (*dl)[i + 1] != (*dl2)[i + 1]) {
2792  printf(
2793  "error: gid=%d expect t=%g %d but queue contains t=%g %d "
2794  "tdiff=%g\n",
2795  gid,
2796  (*dl)[i],
2797  int((*dl)[i + 1]),
2798  (*dl2)[i],
2799  int((*dl2)[i + 1]),
2800  (*dl)[i] - (*dl2)[i]);
2801  }
2802  }
2803  } else {
2804  printf("error: gid=%d distinct delivery times, expect %ld, actual %ld\n",
2805  gid,
2806  dl->size() / 2,
2807  dl2->size() / 2);
2808  }
2809  } else {
2810  printf("error: gid=%d expect spikes but none on queue\n", gid);
2811  for (int i = 0; i < dl->size() - 1; i += 2) {
2812  printf(" %g %d", (*dl)[i], int((*dl)[i + 1]));
2813  }
2814  printf("\n");
2815  }
2816  }
2817  for (const auto& pair: *presyn_queue) {
2818  auto gid = pair.first;
2819  auto dl2 = pair.second;
2820  DblList* dl;
2821  const auto& dliter = presyn_queue->find(gid);
2822  if (dliter == presyn_queue->end()) {
2823  dl = dliter->second;
2824  printf("error: gid=%d expect no spikes but some on queue\n", gid);
2825  for (int i = 0; i < int(dl2->size()) - 1; i += 2) {
2826  printf(" %g %d", (*dl)[i], int((*dl)[i + 1]));
2827  }
2828  printf("\n");
2829  }
2830  }
2831 
2832  // cleanup
2833  if (queuecheck_gid2unc) {
2834  for (const auto& pair: *queuecheck_gid2unc) {
2835  delete pair.second;
2836  }
2837  queuecheck_gid2unc.reset();
2838  }
2839  del_presyn_info();
2840 }
2841 #endif /*QUEUECHECK*/
void nrn_netcon_event(NetCon *, double)
Definition: netcvode.cpp:185
static StateStructInfo * ssi
static void nrnmpi_int_alltoallv(int *s, int *scnt, int *sdispl, int *r, int *rcnt, int *rdispl)
Section ** secorder
Definition: solve.cpp:77
std::unordered_map< std::string, Section * > SecName2Sec
static void scatteritems()
short * nrn_is_artificial_
Definition: init.cpp:193
Point_process * ob2pntproc(Object *)
Definition: hocmech.cpp:88
void(* nrn_binq_enqueue_error_handler)(double, TQItem *)
Definition: sptbinq.cpp:28
static int * giddest
void nrn_gidout_iter(PFIO)
Definition: netpar.cpp:1591
std::vector< TQItem * > TQItemList
static double binq_time(double tt)
std::vector< SEWrap * > SEWrapList
static std::unique_ptr< PP2DE > pp2de
static std::unordered_map< void *, SecName2Sec > pycell_name2sec_maps
static int giddest_size
static TQItemList * tq_removal_list
static std::unique_ptr< NetCon2DblList > nc2dblist
static double vector_play_init(void *v)
static char dbuf[1024]
static int src2send_cnt
static SecName2Sec & pycell_name2sec_map(Object *c)
static void bbss_queuecheck()
int nrn_gid_exists(int gid)
Definition: netpar.cpp:1045
static void nrn_spike_exchange(NrnThread *)
std::unordered_map< NetCon *, DblList * > NetCon2DblList
ReceiveFunc * pnt_receive
Definition: init.cpp:133
Object * nrn_gid2obj(int gid)
Definition: netpar.cpp:1581
static void all2allv_int2(int *scnt, int *sdispl, int *gidsrc, int *ndsrc)
PlayRecList * net_cvode_instance_prl()
Definition: netcvode.cpp:327
static void pycell_name2sec_maps_fill()
static Member_func members[]
static std::unique_ptr< Int2DblList > queuecheck_gid2unc
static void * cons(Object *)
void BBSaveState_reg()
static void allgatherv_helper(int cnt, int *rcnt, int *rdspl)
bool nrn_use_bin_queue_
Definition: netcvode.cpp:273
static void all2allv_dbl1(int *scnt, int *sdispl, double *tssrc)
static bool use_spikecompress_
static BBSaveState * bbss
static void spikes_on_correct_host(int cnt, int *g, int *dcnts, int tscnt, double *ts, Int2DblList *m)
std::unordered_map< int, DblList * > Int2DblList
static void pycell_name2sec_maps_clear()
static void destruct(void *v)
cTemplate ** nrn_pnt_template_
Definition: init.cpp:131
static void bbss_remove_delivered()
void(* ReceiveFunc)(Point_process *, double *, double)
static double save_gid(void *v)
static int ignored(Prop *p)
static double ppignore(void *v)
static void nrnmpi_dbl_alltoallv(double *s, int *scnt, int *sdispl, double *r, int *rcnt, int *rdispl)
static double restore_gid(void *v)
void nrn_play_init()
Definition: cvodestb.cpp:75
static double restore_test_bin(void *v)
static void base2spgid_item(int spgid, Object *obj)
void net_send(void **, double *, Point_process *, double, double)
Definition: netcvode.cpp:2411
static std::unique_ptr< Int2DblList > src2send
double t
Definition: cvodeobj.cpp:59
PreSyn * nrn_gid2presyn(int gid)
Definition: netpar.cpp:1585
hoc_Item * net_cvode_instance_psl()
Definition: netcvode.cpp:323
std::unordered_map< int, int > Int2Int
std::vector< double > DblList
static int usebin_
static void bbss_restore_begin()
static void nrnmpi_dbl_allgatherv(double *s, double *r, int *n, int *dspl)
void(* PFIO)(int, Object *)
Section * nrn_section_exists(char *name, int index, Object *cell)
Definition: cabcode.cpp:2404
int section_count
Definition: solve.cpp:76
static void tqcallback(const TQItem *tq, int i)
static double save(void *v)
static void nrnmpi_int_allgather(int *s, int *r, int n)
static double restore_test(void *v)
static double save_test_bin(void *v)
#define PDEBUG
static std::unique_ptr< Int2Int > base2spgid
void nrn_fake_fire(int gid, double firetime, int fake_out)
Definition: netpar.cpp:905
static int callback_mode
static void ssi_def()
static std::unique_ptr< Int2DblList > presyn_queue
static cTemplate * nct
std::unordered_map< Point_process *, DEList * > PP2DE
void nrn_shape_update()
Definition: treeset.cpp:945
static bool use_gidcompress_
static int debug
TQueue * net_cvode_instance_event_queue(NrnThread *)
Definition: netcvode.cpp:319
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
static double * tsdest
static void del_presyn_info()
static void all2allv_helper(int *scnt, int *sdispl, int *rcnt, int *rdispl)
static void construct_presyn_queue()
static double save_request(void *v)
#define DEBUG
static std::unique_ptr< PointProcessMap > pp_ignore_map
Symlist * hoc_built_in_symlist
Definition: ivocmac.cpp:76
static void nrnmpi_barrier()
static double save_test(void *v)
static int * tsdest_cnts
static TQItemList * tq_presyn_fanout
static void cb_gidobj(int gid, Object *obj)
void clear_event_queue()
Definition: cvodestb.cpp:57
struct DEList DEList
static void nrnmpi_int_allgatherv(int *s, int *r, int *n, int *dspl)
static void bbss_early(double td, TQItem *tq)
static double restore(void *v)
std::unordered_map< Point_process *, int > PointProcessMap
static int nrnmpi_int_allmax(int x)
static SEWrapList * sewrap_list
const char * secname(Section *sec)
Definition: cabcode.cpp:1776
short index
Definition: cabvars.h:10
Memb_func * memb_func
Definition: init.cpp:123
short type
Definition: cabvars.h:9
virtual void s(char *cp, int chk=0)
virtual void cpy(int size, char *cp)
virtual void i(int &j, int chk=0)
BBSS_BufferIn(char *buffer, int size)
virtual ~BBSS_BufferIn()
virtual Type type()
virtual void skip(int n)
virtual void d(int n, double &p)
virtual void i(int &j, int chk=0)
virtual void cpy(int size, char *cp)
virtual ~BBSS_BufferOut()
virtual void s(char *cp, int chk=0)
virtual void a(int)
virtual Type type()
BBSS_BufferOut(char *buffer, int size)
virtual void d(int n, double &p)
virtual void i(int &j, int chk=0)
int bytecntasc()
int bytecnt()
virtual ~BBSS_Cnt()
int bytecntbin()
virtual void s(char *cp, int chk=0)
virtual Type type()
virtual Type type()=0
virtual void d(int n, double &p)=0
virtual void s(char *cp, int chk=0)=0
virtual void skip(int)
Definition: bbsavestate.h:22
virtual void i(int &j, int chk=0)=0
virtual Type type()
BBSS_TxtFileIn(const char *)
virtual void skip(int)
virtual ~BBSS_TxtFileIn()
virtual void s(char *cp, int chk=0)
virtual void d(int n, double &p)
virtual void i(int &j, int chk=0)
virtual Type type()
virtual void i(int &j, int chk=0)
virtual ~BBSS_TxtFileOut()
BBSS_TxtFileOut(const char *)
virtual void s(char *cp, int chk=0)
virtual void d(int n, double &p)
virtual void apply(BBSS_IO *io)
void node01(Section *, Node *)
void mk_presyn_info()
void mech(Prop *)
void node(Node *)
void del_pp2de()
void possible_presyn(int gid)
void cell(Object *)
void mk_base2spgid()
int cellsize(Object *)
void gid2buffer(int gid, char *buffer, int size)
void netrecv_pp(Point_process *)
BBSS_IO * f
Definition: bbsavestate.h:30
void section_exist_info(Section *)
void gidobj(int basegid)
int sectionsize(Section *)
virtual ~BBSaveState()
void buffer2gid(int gid, char *buffer, int size)
void seccontents(Section *)
int counts(int **gids, int **counts)
void section(Section *)
bool flag_
Definition: netcon.h:198
double told_
Definition: netcon.h:195
double valthresh_
Definition: netcon.h:196
double valold_
Definition: netcon.h:195
virtual int type()
Definition: netcon.h:68
virtual void pr(const char *, double t, NetCvode *)
Definition: netcvode.cpp:3169
size_t size() const
Definition: ivocvect.h:43
void resize(size_t n)
Definition: ivocvect.h:47
Definition: netcon.h:83
double * weight_
Definition: netcon.h:112
int cnt_
Definition: netcon.h:114
double delay_
Definition: netcon.h:109
Point_process * target_
Definition: netcon.h:111
PreSyn * src_
Definition: netcon.h:110
int ithread_
Definition: netcon.h:411
virtual void savestate_restore(double deliverytime, NetCvode *)
Definition: netpar.cpp:303
int prop_index(const Symbol *) const
Definition: ndatclas.cpp:215
int var_type(Symbol *) const
Definition: ndatclas.cpp:153
Symbol * first_var()
Definition: ndatclas.cpp:127
Symbol * next_var()
Definition: ndatclas.cpp:140
bool more_var()
Definition: ndatclas.cpp:132
Prop * prop() const
Definition: ndatclas.cpp:123
Definition: netcon.h:255
int output_index_
Definition: netcon.h:306
Section * ssrc_
Definition: netcon.h:296
IvocVect * idvec_
Definition: netcon.h:298
void fanout(double, NetCvode *, NrnThread *)
Definition: netcvode.cpp:3363
int gid_
Definition: netcon.h:307
double delay_
Definition: netcon.h:293
Object * osrc_
Definition: netcon.h:295
IvocVect * tvec_
Definition: netcon.h:297
NetConPList dil_
Definition: netcon.h:291
int type()
SelfEvent * se
SEWrap(const TQItem *, DEList *)
double tt
virtual int type()
Definition: netcon.h:155
void ** movable_
Definition: netcon.h:167
double * weight_
Definition: netcon.h:166
double flag_
Definition: netcon.h:164
Definition: bbtqueue.h:6
double t_
Definition: bbtqueue.h:23
void * data_
Definition: bbtqueue.h:22
void remove(TQItem *)
Definition: bbtqueue.cpp:236
double least_t()
Definition: bbtqueue.cpp:131
TQItem * least()
Definition: bbtqueue.cpp:140
void shift_bin(double t)
Definition: sptbinq.h:113
void forall_callback(void(*)(const TQItem *, int))
Definition: bbtqueue.cpp:115
double tbin()
Definition: sptbinq.h:117
Symbol * hoc_table_lookup(const char *, Symlist *)
Definition: symbol.cpp:61
double dt
Definition: netcvode.cpp:76
int v_structure_change
Definition: cvodestb.cpp:99
sprintf(buf, " if (secondorder) {\n" " int _i;\n" " for (_i = 0; _i < %d; ++_i) {\n" " _p[_slist%d[_i]] += dt*_p[_dlist%d[_i]];\n" " }}\n", numeqn, listnum, listnum)
static int indx
Definition: deriv.cpp:294
#define c
#define chk(i)
Definition: fmenu.cpp:193
static int first
Definition: fmenu.cpp:190
char buf[512]
Definition: init.cpp:13
void bbss_restore_global(void *bbss, char *buffer, int sz)
void * nrn_opaque_obj2pyobj(Object *ho)
Definition: hoc_oop.cpp:2068
void hoc_execerr_ext(const char *fmt,...)
printf style specification of hoc_execerror message.
Definition: fileio.cpp:931
void bbss_restore(void *bbss, int gid, int npiece, char *buffer, int sz)
size_t hoc_total_array_data(Symbol *s, Objectdata *obd)
Definition: hoc_oop.cpp:94
void hoc_pushpx(double *d)
Definition: code.cpp:716
void hoc_call_ob_proc(Object *ob, Symbol *sym, int narg)
void bbss_restore_done(void *bbss)
void bbss_save(void *bbss, int gid, char *buffer, int sz)
Vect * vector_arg(int i)
Definition: ivocvect.cpp:397
char * hoc_object_name(Object *ob)
Definition: hoc_oop.cpp:72
void bbss_save_done(void *bbss)
void * bbss_buffer_counts(int *len, int **gids, int **sizes, int *global_size)
Symbol * hoc_lookup(const char *)
double hoc_xpop(void)
void bbss_save_global(void *bbss, char *buffer, int sz)
void hoc_obj_unref(Object *obj)
Definition: hoc_oop.cpp:1828
#define assert(ex)
Definition: hocassrt.h:32
#define gargstr
Definition: hocdec.h:14
#define hocSEC(q)
Definition: hoclist.h:66
#define OBJ(q)
Definition: hoclist.h:67
Object ** hoc_objgetarg(int)
Definition: code.cpp:1587
void
int ifarg(int)
Definition: code.cpp:1581
static int narg()
Definition: ivocvect.cpp:150
#define Vect
Definition: ivocvect.h:14
#define v
Definition: md1redef.h:4
#define sec
Definition: md1redef.h:13
#define i
Definition: md1redef.h:12
#define _AMBIGUOUS
Definition: membfunc.h:100
#define STATE
Definition: membfunc.h:71
#define ITERATE(itm, lst)
Definition: model.h:25
fabs
Definition: extdef.h:3
char * name
Definition: init.cpp:16
Item * next(Item *item)
Definition: list.cpp:88
NrnThread * nrn_threads
Definition: multicore.cpp:47
#define printf
Definition: mwprefix.h:26
#define fprintf
Definition: mwprefix.h:30
#define NetParEventType
Definition: netcon.h:47
#define PreSynType
Definition: netcon.h:43
#define SelfEventType
Definition: netcon.h:42
#define NetConType
Definition: netcon.h:41
Object * nrn_sec2cell(Section *)
Definition: cabcode.cpp:233
int is_point_process(Object *)
Definition: point.cpp:396
#define nrn_assert(ex)
Definition: nrnassrt.h:53
int const size_t const size_t n
Definition: nrngsl.h:11
size_t q
if(status)
size_t p
size_t j
void nrnmpi_abort(int errcode)
Definition: nrnmpi.cpp:205
int nrnmpi_myid
int nrnmpi_numprocs
hoc_List * section_list
Definition: init.cpp:102
double nrn_call_mech_func(Symbol *s, int narg, Prop *p, int type)
Definition: init.cpp:1049
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:1560
int n_memb_func
Definition: init.cpp:440
static philox4x32_key_t k
Definition: nrnran123.cpp:11
static double ref(void *v)
Definition: ocbox.cpp:383
static double cell(void *v)
Definition: ocbbs.cpp:578
#define g
Definition: passive0.cpp:21
#define e
Definition: passive0.cpp:22
#define add
Definition: redef.h:24
#define PROP_PY_INDEX
Definition: section.h:212
#define NODEV(n)
Definition: section.h:115
FILE * fopen()
#define cnt
Definition: spt2queue.cpp:19
#define NULL
Definition: sptree.h:16
DiscreteEvent * de
struct DEList * next
int is_point
Definition: membfunc.h:53
Definition: section.h:133
struct Prop * prop
Definition: section.h:152
Represent main neuron object computed by single thread.
Definition: multicore.h:58
double _dt
Definition: multicore.h:60
double _t
Definition: multicore.h:59
Definition: hocdec.h:227
HocStruct hoc_Item * secelm_
Definition: hocdec.h:242
Object * ob
Definition: section.h:267
Section * sec
Definition: section.h:263
Prop * prop
Definition: section.h:265
Definition: section.h:214
Datum * dparam
Definition: section.h:220
int param_size
Definition: section.h:218
short type
Definition: section.h:216
Definition: model.h:57
union Symbol::@18 u
char * name
Definition: model.h:72
HocStruct cTemplate * ctemplate
Definition: hocdec.h:152
Definition: hocdec.h:84
Symlist * symtable
Definition: hocdec.h:197
hoc_List * olist
Definition: hocdec.h:204
int count
Definition: hocdec.h:203
struct hoc_Item * next
Definition: hoclist.h:50
Definition: hocdec.h:177
void * _pvoid
Definition: hocdec.h:187
static const char * fname(const char *name)
Definition: nrnbbs.cpp:113