NEURON
nrncore_write.cpp
Go to the documentation of this file.
1 #include "nrnconf.h"
2 // A model built using NEURON is heavyweight in memory usage and that
3 // prevents maximizing the number of cells that can be simulated on
4 // a process. On the other hand, a tiny version of NEURON that contains
5 // only the cache efficient structures, minimal memory usage arrays,
6 // needed to do a simulation (no interpreter, hoc Objects, Section, etc.)
7 // lacks the model building flexibility of NEURON.
8 // Ideally, the only arrays needed for a tiny version simulation are those
9 // enumerated in the NrnThread structure in src/nrnoc/multicore.h up to,
10 // but not including, the Node** arrays. Also tiny versions of POINT_PROCESS,
11 // PreSyn, NetCon, and SUFFIX mechanisms will be stripped down from
12 // their full NEURON definitions and, it seems certain, many of the
13 // double fields will be converted to some other, less memory using, types.
14 // With this in mind, we envision that NEURON will incrementally construct
15 // cache efficient whole cell structures which can be saved and read with
16 // minimal processing into the tiny simulator. Note that this is a petabyte
17 // level of data volume. Consider, for example, 128K cores each
18 // preparing model data for several thousand cells using full NEURON where
19 // there is not enough space for the simultaneous existence of
20 // those several thousand cells --- but there is with the tiny version.
21 
22 // Several assumptions with regard to the nrnbbcore_read reader.
23 // Since memory is filled with cells, whole cell
24 // load balance should be adequate and so there is no provision for
25 // multisplit. A process gets a list of the gid owned by that process
26 // and allocates the needed
27 // memory based on size variables for each gid, i.e.
28 // number of nodes, number of instances of each mechanism type, and number
29 // of NetCon instances. Also the offsets are calculated for where the
30 // cell information is to reside in the cache efficient arrays.
31 // The rest of the cell information is then copied
32 // into memory with the proper offsets. Pointers to data, used in the old
33 // NEURON world are converted to integer indices into a common data array.
34 
35 // A good deal of conceptual confusion resulted in earlier implementations
36 // with regard to ordering of synapses and
37 // artificial cells with and without gids. The ordering of the property
38 // data for those is defined by the order in the NrnThread.tml list where
39 // every Memb_list.data has an easily found index relative to its 'nodecount'.
40 // (For artificial cells, since those are not ordered in a cache efficient
41 // array, we get the index using int nrncore_art2index(double* param)
42 // which looks up the index in a hash table. Earlier implementations
43 // handled 'artificial cells without gids' specially which also
44 // necessitated special handling of their NetCons and disallowed artificial
45 // cells with gids. We now handle all artificial cells in a thread
46 // in the same way as any other synapse (the assumption still holds that
47 // any artificial cell without a gid in a thread can connect only to
48 // targets in the same thread. Thus, a single NrnThread.synapses now contains
49 // all synapses and all artificial cells belonging to that thread. All
50 // the synapses and artificial cells are in NrnThread.tml order. So there
51 // are no exceptions in filling Point_process pointers from the data indices
52 // on the coreneuron side. PreSyn ordering is a bit more delicate.
53 // From netpar.cpp, the gid2out_ hash table defines an output_gid
54 // ordering and gives us all the PreSyn
55 // associated with real and artificial cells having gids. But those are
56 // randomly ordered and interleaved with 'no gid instances'
57 // relative to the tml ordering.
58 // Since the number of output PreSyn >= number of output_gid it makes sense
59 // to order the PreSyn in the same way as defined by the tml ordering.
60 // Thus, even though artificial cells with and without gids are mixed,
61 // at least it is convenient to fill the PreSyn.psrc field.
62 // Synapses are first but the artificial cells with and without gids are
63 // mixed. The problem that needs to
64 // be explicitly overcome is associating output gids with the proper PreSyn
65 // and that can be done with a list parallel to the acell part of the
66 // output_gid list that specifies the PreSyn list indices.
67 // Note that allocation of large arrays allows considerable space savings
68 // by eliminating overhead involved in allocation of many individual
69 // instances.
70 /*
71 Assumptions regarding the scope of possible models.(Incomplete list)
72 All real cells have gids.
73 Artificial cells without gids connect only to cells in the same thread.
74 No POINTER to data outside of NrnThread.
75 No POINTER to data in ARTIFICIAL_CELL (that data is not cache_efficient)
76 nt->tml->pdata is not cache_efficient
77 */
78 // See coreneuron/nrniv/nrn_setup.cpp for a description of
79 // the file format written by this file.
80 
81 /*
82 Support direct transfer of model to dynamically loaded coreneuron library.
83 To do this we factored all major file writing components into a series
84 of functions that return data that can be called from the coreneuron
85 library. The file writing functionality is kept by also calling those
86 functions here as well.
87 Direct transfer mode disables error checking with regard to every thread
88 having a real cell with a gid. Of course real and artificial cells without
89 gids do not have spike information in the output raster file. Trajectory
90 correctness has not been validated for cells without gids.
91 */
92 #include <cstdlib>
93 
94 #include "section.h"
95 #include "parse.hpp"
96 #include "nrnmpi.h"
97 #include "netcon.h"
98 #include "nrncvode.h"
99 
100 #include "vrecitem.h" // for nrnbbcore_vecplay_write
101 #include "nrnsection_mapping.h"
102 
103 #include "nrncore_write.h"
107 #include <map>
108 
109 
110 #ifdef MINGW
111 #define RTLD_NOW 0
112 #define RTLD_GLOBAL 0
113 #define RTLD_NOLOAD 0
114 extern "C" {
115 extern void* dlopen_noerr(const char* name, int mode);
116 #define dlopen dlopen_noerr
117 extern void* dlsym(void* handle, const char* name);
118 extern int dlclose(void* handle);
119 extern char* dlerror();
120 }
121 #else
122 #if defined(HAVE_DLFCN_H)
123 #include <dlfcn.h>
124 #endif
125 #endif
126 
127 
129 
130 
131 extern int* nrn_prop_dparam_size_;
132 int* bbcore_dparam_size; // cvodeieq not present
133 extern double t; // see nrncore_psolve
134 
135 /* not NULL, need to write gap information */
137 extern size_t nrnbbcore_gap_write(const char* path, int* group_ids);
138 
139 extern size_t nrncore_netpar_bytes();
140 extern short* nrn_is_artificial_;
141 
144 
145 char* (*nrnpy_nrncore_arg_p_)(double tstop);
146 
148 /** mapping information */
150 
151 
152 // direct transfer or via files? The latter makes use of group_gid for
153 // filename construction.
155 
156 // name of coreneuron mpi library to load
158 
159 static size_t part1();
160 static void part2(const char*);
161 
162 /// dump neuron model to given directory path
163 size_t write_corenrn_model(const std::string& path) {
164 
165  // if writing to disk then in-memory mode is false
166  corenrn_direct = false;
167 
168  // make sure model is ready to transfer
169  model_ready();
170 
171  // directory to write model
172  create_dir_path(path);
173 
174  // calculate size of the model
175  size_t rankbytes = part1();
176 
177  // mechanism and global variables
178  write_memb_mech_types(get_filename(path, "bbcore_mech.dat").c_str());
179  write_globals(get_filename(path, "globals.dat").c_str());
180 
181  // write main model data
182  part2(path.c_str());
183 
184  return rankbytes;
185 }
186 
187 // accessible from ParallelContext.total_bytes()
188 size_t nrncore_write() {
189  const std::string& path = get_write_path();
190  return write_corenrn_model(path);
191 }
192 
193 static size_t part1() {
194  size_t rankbytes = 0;
195  if (!bbcore_dparam_size) {
196  bbcore_dparam_size = new int[n_memb_func];
197  }
198  for (int i=0; i < n_memb_func; ++i) {
199  int sz = nrn_prop_dparam_size_[i];
200  bbcore_dparam_size[i] = sz;
201  Memb_func* mf = memb_func + i;
202  if (mf && mf->dparam_semantics && sz && mf->dparam_semantics[sz-1] == -3) {
203  // cvode_ieq in NEURON but not CoreNEURON
204  bbcore_dparam_size[i] = sz - 1;
205  }
206  }
208  cellgroups_ = new CellGroup[nrn_nthread]; // here because following needs mlwithart
209  CellGroup::mk_tml_with_art(cellgroups_);
210 
211  rankbytes += CellGroup::get_mla_rankbytes(cellgroups_);
212  rankbytes += nrncore_netpar_bytes();
213  //printf("%d bytes %ld\n", nrnmpi_myid, rankbytes);
214  CellGroup* cgs = CellGroup::mk_cellgroups(cellgroups_);
215 
217  return rankbytes;
218 }
219 
220 static void part2(const char* path) {
222  for (int i=0; i < nrn_nthread; ++i) {
223  chkpnt = 0;
224  write_nrnthread(path, nrn_threads[i], cgs[i]);
225  }
226 
227  /** write mapping information */
228  if(mapinfo.size()) {
229  int gid = cgs[0].group_id;
230  nrn_write_mapping_info(path, gid, mapinfo);
231  mapinfo.clear();
232  }
233 
234  if (nrnthread_v_transfer_) {
235  // see partrans.cpp. nrn_nthread files of path/icg_gap.dat
236  int* group_ids = new int[nrn_nthread];
237  for (int i=0; i < nrn_nthread; ++i) {
238  group_ids[i] = cgs[i].group_id;
239  }
240  nrnbbcore_gap_write(path, group_ids);
241  delete [] group_ids;
242  }
243 
244  // filename data might have to be collected at hoc level since
245  // pc.nrncore_write might be called
246  // many times per rank since model may be built as series of submodels.
247  if (ifarg(2) && hoc_is_object_arg(2) && is_vector_arg(2)) {
248  // Legacy style. Interpreter collects groupgids and writes files.dat
249  Vect* cgidvec = vector_arg(2);
250  vector_resize(cgidvec, nrn_nthread);
251  double* px = vector_vec(cgidvec);
252  for (int i=0; i < nrn_nthread; ++i) {
253  px[i] = double(cgs[i].group_id);
254  }
255  }else{
256  bool append = false;
257  if (ifarg(2)) {
258  if (hoc_is_double_arg(2)) {
259  append = (*getarg(2) != 0);
260  }else{
261  hoc_execerror("Second arg must be Vector or double.", NULL);
262  }
263  }
264  write_nrnthread_task(path, cgs, append);
265  }
266 
267  part2_clean();
268 }
269 
270 
271 #if defined(HAVE_DLFCN_H)
272 /** Launch CoreNEURON in direct memory mode */
273 int nrncore_run(const char* arg) {
274  // using direct memory mode
275  corenrn_direct = true;
276 
277  // check that model can be transferred
278  model_ready();
279 
280  // get coreneuron library handle
281  void* handle = get_coreneuron_handle();
282 
283  // make sure coreneuron & neuron are compatible
284  check_coreneuron_compatibility(handle);
285 
286  // setup the callback functions between neuron & coreneuron
287  map_coreneuron_callbacks(handle);
288 
289  // lookup symbol from coreneuron for launching
290  void* launcher_sym = dlsym(handle, "corenrn_embedded_run");
291  if (!launcher_sym) {
292  hoc_execerror("Could not get symbol corenrn_embedded_run from", NULL);
293  }
294 
295  // prepare the model
296  part1();
297 
298  int have_gap = nrnthread_v_transfer_ ? 1 : 0;
299 #if !NRNMPI
300 #define nrnmpi_use 0
301 #endif
302 
303  // typecast function pointer pointer
304  int (*coreneuron_launcher)(int, int, int, int, const char*, const char*) = (int (*)(int, int, int, int, const char*, const char*))launcher_sym;
305 
306  // launch coreneuron
307  int result = coreneuron_launcher(nrn_nthread, have_gap, nrnmpi_use, nrn_use_fast_imem, corenrn_mpi_library.c_str(), arg);
308 
309  // close handle and return result
310  dlclose(handle);
311 
312  // Note: possibly non-empty only if nrn_nthread > 1
314 
315  // Huge memory waste
317 
318  return result;
319 }
320 
321 /** Return neuron.coreneuron.enable */
322 int nrncore_is_enabled() {
324  int b = (*nrnpy_nrncore_enable_value_p_)();
325  return b;
326  }
327  return 0;
328 }
329 
330 /** Return value of neuron.coreneuron.file_mode flag */
331 int nrncore_is_file_mode() {
333  int result = (*nrnpy_nrncore_file_mode_value_p_)();
334  return result;
335  }
336  return 0;
337 }
338 
339 /** Run coreneuron with arg string from neuron.coreneuron.nrncore_arg(tstop)
340  * Return 0 on success
341  */
342 int nrncore_psolve(double tstop, int file_mode) {
343  if (nrnpy_nrncore_arg_p_) {
344  char* arg = (*nrnpy_nrncore_arg_p_)(tstop);
345  if (arg) {
346  // if file mode is requested then write model to a directory
347  // note that CORENRN_DATA_DIR name is also used in module
348  // file coreneuron.py
349  if (file_mode) {
350  const char* CORENRN_DATA_DIR = "corenrn_data";
351  write_corenrn_model(CORENRN_DATA_DIR);
352  }
353  nrncore_run(arg);
354  // data return nt._t so copy to t
355  t = nrn_threads[0]._t;
356  free(arg);
357  // Really just want to get NetParEvent back onto queue.
359  return 0;
360  }
361  }
362  return -1;
363 }
364 
365 #else // !HAVE_DLFCN_H
366 
367 int nrncore_run(const char *) {
368  return -1;
369 }
370 
372  return 0;
373 }
374 
376  return 0;
377 }
378 
379 int nrncore_psolve(double tstop, int file_mode) {
380  return 0;
381 }
382 
383 #endif //!HAVE_DLFCN_H
int nrn_use_fast_imem
Definition: fadvance.cpp:162
static void datumtransform(CellGroup *)
Definition: cell_group.cpp:164
#define Vect
Definition: ivocvect.h:14
int group_id
Definition: cell_group.h:25
CellGroup * cellgroups_
const char * dlerror(void)
Definition: osxdlfcn.cpp:174
int nrncore_is_file_mode()
int hoc_is_double_arg(int narg)
Definition: code.cpp:733
void clear()
after writing NrnThread to file we remove all previous mapping information, free memory.
void
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int(* nrnpy_nrncore_enable_value_p_)()
value of neuron.coreneuron.enable as 0, 1 (-1 if error)
int(* nrnpy_nrncore_file_mode_value_p_)()
value of neuron.coreneuron.file_mode as 0, 1 (-1 if error)
static int nrnmpi_use
Definition: multisplit.cpp:48
Memb_func * memb_func
Definition: init.cpp:161
void write_memb_mech_types(const char *fname)
Definition: nrncore_io.cpp:58
int nrncore_psolve(double tstop, int file_mode)
void nrn_spike_exchange_init()
Definition: netpar.cpp:441
std::string get_write_path()
Definition: nrncore_io.cpp:43
int * bbcore_dparam_size
int dlclose(void *handle)
Definition: osxdlfcn.cpp:178
int is_vector_arg(int i)
Definition: ivocvect.cpp:340
int nrncore_is_enabled()
size_t nrnbbcore_gap_write(const char *path, int *group_ids)
Definition: partrans.cpp:1039
int nrncore_run(const char *)
short * nrn_is_artificial_
Definition: init.cpp:231
void append(Item *ql, Item *q)
Definition: list.cpp:348
int nrn_nthread
Definition: multicore.cpp:44
static void mk_tml_with_art(CellGroup *)
Definition: cell_group.cpp:469
static size_t part1()
NrnThread * nrn_threads
Definition: multicore.cpp:45
void(* nrnthread_v_transfer_)(NrnThread *)
Definition: fadvance.cpp:148
static void part2(const char *)
size_t write_corenrn_model(const std::string &path)
dump neuron model to given directory path
int
Definition: nrnmusic.cpp:71
static void clean_deferred_netcons()
void model_ready()
int n_memb_func
Definition: init.cpp:471
void hoc_execerror(const char *, const char *)
Definition: hoc.cpp:741
void write_nrnthread(const char *path, NrnThread &nt, CellGroup &cg)
Definition: nrncore_io.cpp:114
char *(* nrnpy_nrncore_arg_p_)(double tstop)
Gets the python string returned by neuron.coreneuron.nrncore_arg(tstop) return a strdup() copy of the...
bool corenrn_direct
NrnMappingInfo mapinfo
mapping information
Compartment mapping information for NrnThread.
size_t size()
number of cells
char * name
Definition: init.cpp:16
std::string corenrn_mpi_library
static CellGroup * mk_cellgroups(CellGroup *)
Definition: cell_group.cpp:54
std::string get_filename(const std::string &path, std::string file_name)
Definition: nrncore_io.cpp:51
void write_globals(const char *fname)
Definition: nrncore_io.cpp:72
static size_t get_mla_rankbytes(CellGroup *)
Definition: cell_group.cpp:539
int ifarg(int)
Definition: code.cpp:1562
void vector_resize(Vect *v, int n)
Definition: ivocvect.cpp:269
void create_dir_path(const std::string &path)
create directory with given path
Definition: nrncore_io.cpp:28
int * nrn_prop_dparam_size_
Definition: init.cpp:179
VEC * cgs(MTX_FN A, void *A_params, VEC *b, VEC *r0, double tol, VEC *x)
Definition: conjgrad.c:179
Vect * vector_arg(int i)
Definition: ivocvect.cpp:332
double _t
Definition: multicore.h:59
static void clean_deferred_type2artml()
Definition: cell_group.h:64
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
size_t nrncore_write()
#define getarg
Definition: hocdec.h:15
void part2_clean()
size_t nrncore_netpar_bytes()
Definition: netpar.cpp:1525
#define i
Definition: md1redef.h:12
double * vector_vec(Vect *v)
Definition: ivocvect.cpp:271
#define arg
Definition: redef.h:28
void map_coreneuron_callbacks(void *handle)
Populate function pointers by mapping function pointers for callback.
double t
Definition: init.cpp:123
int hoc_is_object_arg(int narg)
Definition: code.cpp:745
int * dparam_semantics
Definition: membfunc.h:56
void * dlsym(void *handle, const char *symbol)
Definition: osxdlfcn.cpp:193
int chkpnt
Definition: nrncore_io.cpp:24
void write_nrnthread_task(const char *path, CellGroup *cgs, bool append)
Write all dataset ids to files.dat.
Definition: nrncore_io.cpp:348
return NULL
Definition: cabcode.cpp:461
void nrn_write_mapping_info(const char *path, int gid, NrnMappingInfo &minfo)
dump mapping information to gid_3.dat file
Definition: nrncore_io.cpp:502
static void setup_nrn_has_net_event()
Definition: cell_group.cpp:613