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 #include "nrnwrap_dlfcn.h"
110 
111 
113 
114 
115 extern int* nrn_prop_dparam_size_;
116 int* bbcore_dparam_size; // cvodeieq not present
117 extern double t; // see nrncore_psolve
118 
119 /* not NULL, need to write gap information */
121 extern size_t nrnbbcore_gap_write(const char* path, int* group_ids);
122 
123 extern size_t nrncore_netpar_bytes();
124 extern short* nrn_is_artificial_;
125 
128 
129 char* (*nrnpy_nrncore_arg_p_)(double tstop);
130 
132 /** mapping information */
134 
135 
136 // direct transfer or via files? The latter makes use of group_gid for
137 // filename construction.
139 
140 // name of coreneuron mpi library to load
142 
143 static size_t part1();
144 static void part2(const char*);
145 
146 /// dump neuron model to given directory path
147 size_t write_corenrn_model(const std::string& path) {
148  // if writing to disk then in-memory mode is false
149  corenrn_direct = false;
150 
151  // make sure model is ready to transfer
152  model_ready();
153 
154  // directory to write model
155  create_dir_path(path);
156 
157  // calculate size of the model
158  size_t rankbytes = part1();
159 
160  // mechanism and global variables
161  write_memb_mech_types(get_filename(path, "bbcore_mech.dat").c_str());
162  write_globals(get_filename(path, "globals.dat").c_str());
163 
164  // write main model data
165  part2(path.c_str());
166 
167  return rankbytes;
168 }
169 
170 // accessible from ParallelContext.total_bytes()
171 size_t nrncore_write() {
172  const std::string& path = get_write_path();
173  return write_corenrn_model(path);
174 }
175 
176 static size_t part1() {
177  size_t rankbytes = 0;
178  if (!bbcore_dparam_size) {
179  bbcore_dparam_size = new int[n_memb_func];
180  }
181  for (int i = 0; i < n_memb_func; ++i) {
182  int sz = nrn_prop_dparam_size_[i];
183  bbcore_dparam_size[i] = sz;
184  Memb_func* mf = memb_func + i;
185  if (mf && mf->dparam_semantics && sz && mf->dparam_semantics[sz - 1] == -3) {
186  // cvode_ieq in NEURON but not CoreNEURON
187  bbcore_dparam_size[i] = sz - 1;
188  }
189  }
191  cellgroups_ = new CellGroup[nrn_nthread]; // here because following needs mlwithart
193 
195  rankbytes += nrncore_netpar_bytes();
196  // printf("%d bytes %ld\n", nrnmpi_myid, rankbytes);
198 
200  return rankbytes;
201 }
202 
203 static void part2(const char* path) {
205  for (int i = 0; i < nrn_nthread; ++i) {
206  chkpnt = 0;
207  write_nrnthread(path, nrn_threads[i], cgs[i]);
208  }
209 
210  /** write mapping information */
211  if (mapinfo.size()) {
212  int gid = cgs[0].group_id;
213  nrn_write_mapping_info(path, gid, mapinfo);
214  mapinfo.clear();
215  }
216 
217  if (nrnthread_v_transfer_) {
218  // see partrans.cpp. nrn_nthread files of path/icg_gap.dat
219  int* group_ids = new int[nrn_nthread];
220  for (int i = 0; i < nrn_nthread; ++i) {
221  group_ids[i] = cgs[i].group_id;
222  }
223  nrnbbcore_gap_write(path, group_ids);
224  delete[] group_ids;
225  }
226 
227  // filename data might have to be collected at hoc level since
228  // pc.nrncore_write might be called
229  // many times per rank since model may be built as series of submodels.
230  if (ifarg(2) && hoc_is_object_arg(2) && is_vector_arg(2)) {
231  // Legacy style. Interpreter collects groupgids and writes files.dat
232  Vect* cgidvec = vector_arg(2);
233  vector_resize(cgidvec, nrn_nthread);
234  double* px = vector_vec(cgidvec);
235  for (int i = 0; i < nrn_nthread; ++i) {
236  px[i] = double(cgs[i].group_id);
237  }
238  } else {
239  bool append = false;
240  if (ifarg(2)) {
241  if (hoc_is_double_arg(2)) {
242  append = (*getarg(2) != 0);
243  } else {
244  hoc_execerror("Second arg must be Vector or double.", NULL);
245  }
246  }
248  }
249 
250  part2_clean();
251 }
252 
253 
254 #if defined(HAVE_DLFCN_H)
255 /** Launch CoreNEURON in direct memory mode */
256 int nrncore_run(const char* arg) {
257  // using direct memory mode
258  corenrn_direct = true;
259 
260  // check that model can be transferred
261  model_ready();
262 
263  // get coreneuron library handle
264  void* handle = get_coreneuron_handle();
265 
266  // make sure coreneuron & neuron are compatible
267  check_coreneuron_compatibility(handle);
268 
269  // setup the callback functions between neuron & coreneuron
270  map_coreneuron_callbacks(handle);
271 
272  // lookup symbol from coreneuron for launching
273  void* launcher_sym = dlsym(handle, "corenrn_embedded_run");
274  if (!launcher_sym) {
275  hoc_execerror("Could not get symbol corenrn_embedded_run from", NULL);
276  }
277 
278  // prepare the model
279  part1();
280 
281  int have_gap = nrnthread_v_transfer_ ? 1 : 0;
282 #if !NRNMPI
283 #define nrnmpi_use 0
284 #endif
285 
286  // typecast function pointer pointer
287  int (*coreneuron_launcher)(int, int, int, int, const char*, const char*) =
288  (int (*)(int, int, int, int, const char*, const char*)) launcher_sym;
289 
290  // launch coreneuron
291  int result = coreneuron_launcher(
293 
294  // close handle and return result
295  dlclose(handle);
296 
297  // Note: possibly non-empty only if nrn_nthread > 1
299 
300  // Huge memory waste
302 
303  return result;
304 }
305 
306 /** Return neuron.coreneuron.enable */
307 int nrncore_is_enabled() {
309  int b = (*nrnpy_nrncore_enable_value_p_)();
310  return b;
311  }
312  return 0;
313 }
314 
315 /** Return value of neuron.coreneuron.file_mode flag */
316 int nrncore_is_file_mode() {
318  int result = (*nrnpy_nrncore_file_mode_value_p_)();
319  return result;
320  }
321  return 0;
322 }
323 
324 /** Run coreneuron with arg string from neuron.coreneuron.nrncore_arg(tstop)
325  * Return 0 on success
326  */
327 int nrncore_psolve(double tstop, int file_mode) {
328  if (nrnpy_nrncore_arg_p_) {
329  char* arg = (*nrnpy_nrncore_arg_p_)(tstop);
330  if (arg) {
331  // if file mode is requested then write model to a directory
332  // note that CORENRN_DATA_DIR name is also used in module
333  // file coreneuron.py
334  if (file_mode) {
335  const char* CORENRN_DATA_DIR = "corenrn_data";
336  write_corenrn_model(CORENRN_DATA_DIR);
337  }
338  nrncore_run(arg);
339  // data return nt._t so copy to t
340  t = nrn_threads[0]._t;
341  free(arg);
342  // Really just want to get NetParEvent back onto queue.
344  return 0;
345  }
346  }
347  return -1;
348 }
349 
350 #else // !HAVE_DLFCN_H
351 
352 int nrncore_run(const char*) {
353  return -1;
354 }
355 
357  return 0;
358 }
359 
361  return 0;
362 }
363 
364 int nrncore_psolve(double tstop, int file_mode) {
365  return 0;
366 }
367 
368 #endif //! HAVE_DLFCN_H
Memb_func * memb_func
Definition: init.cpp:123
static void clean_deferred_netcons()
static void clean_deferred_type2artml()
Definition: cell_group.h:65
static CellGroup * mk_cellgroups(CellGroup *)
Definition: cell_group.cpp:63
static void mk_tml_with_art(CellGroup *)
Definition: cell_group.cpp:495
static void setup_nrn_has_net_event()
Definition: cell_group.cpp:645
static size_t get_mla_rankbytes(CellGroup *)
Definition: cell_group.cpp:571
static void datumtransform(CellGroup *)
Definition: cell_group.cpp:173
VEC * cgs(MTX_FN A, void *A_params, VEC *b, VEC *r0, double tol, VEC *x)
Definition: conjgrad.c:179
DLFCN_EXPORT int dlclose(void *handle)
Definition: dlfcn.c:423
DLFCN_NOINLINE DLFCN_EXPORT void * dlsym(void *handle, const char *name)
Definition: dlfcn.c:447
int nrn_use_fast_imem
Definition: fadvance.cpp:167
void hoc_execerror(const char *, const char *)
Definition: hoc.cpp:754
int hoc_is_object_arg(int narg)
Definition: code.cpp:756
int hoc_is_double_arg(int narg)
Definition: code.cpp:744
Vect * vector_arg(int i)
Definition: ivocvect.cpp:397
int is_vector_arg(int i)
Definition: ivocvect.cpp:405
#define getarg
Definition: hocdec.h:15
void
int ifarg(int)
Definition: code.cpp:1581
void vector_resize(Vect *v, int n)
Definition: ivocvect.cpp:322
double * vector_vec(Vect *v)
Definition: ivocvect.cpp:328
#define Vect
Definition: ivocvect.h:14
#define i
Definition: md1redef.h:12
void append(Item *ql, Item *q)
Definition: list.cpp:318
int nrn_nthread
Definition: multicore.cpp:46
NrnThread * nrn_threads
Definition: multicore.cpp:47
static int nrnmpi_use
Definition: multisplit.cpp:48
void nrn_spike_exchange_init()
Definition: netpar.cpp:458
void map_coreneuron_callbacks(void *handle)
Populate function pointers by mapping function pointers for callback.
void part2_clean()
void write_memb_mech_types(const char *fname)
Definition: nrncore_io.cpp:59
void create_dir_path(const std::string &path)
create directory with given path
Definition: nrncore_io.cpp:29
std::string get_filename(const std::string &path, std::string file_name)
Definition: nrncore_io.cpp:52
void write_globals(const char *fname)
Definition: nrncore_io.cpp:75
void nrn_write_mapping_info(const char *path, int gid, NrnMappingInfo &minfo)
dump mapping information to gid_3.dat file
Definition: nrncore_io.cpp:540
void write_nrnthread_task(const char *path, CellGroup *cgs, bool append)
Write all dataset ids to files.dat.
Definition: nrncore_io.cpp:375
void write_nrnthread(const char *path, NrnThread &nt, CellGroup &cg)
Definition: nrncore_io.cpp:118
int chkpnt
Definition: nrncore_io.cpp:24
std::string get_write_path()
Definition: nrncore_io.cpp:44
void model_ready()
static void part2(const char *)
int nrncore_run(const char *)
short * nrn_is_artificial_
Definition: init.cpp:193
CellGroup * cellgroups_
std::string corenrn_mpi_library
size_t nrnbbcore_gap_write(const char *path, int *group_ids)
Definition: partrans.cpp:1123
size_t write_corenrn_model(const std::string &path)
dump neuron model to given directory path
void(* nrnthread_v_transfer_)(NrnThread *)
Definition: fadvance.cpp:153
int nrncore_is_enabled()
int nrncore_is_file_mode()
bool corenrn_direct
int nrncore_psolve(double tstop, int file_mode)
int(* nrnpy_nrncore_file_mode_value_p_)()
value of neuron.coreneuron.file_mode as 0, 1 (-1 if error)
double t
Definition: cvodeobj.cpp:59
int * bbcore_dparam_size
size_t nrncore_write()
char *(* nrnpy_nrncore_arg_p_)(double tstop)
Gets the python string returned by neuron.coreneuron.nrncore_arg(tstop) return a strdup() copy of the...
int * nrn_prop_dparam_size_
Definition: init.cpp:141
static size_t part1()
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:27
int(* nrnpy_nrncore_enable_value_p_)()
value of neuron.coreneuron.enable as 0, 1 (-1 if error)
size_t nrncore_netpar_bytes()
Definition: netpar.cpp:1611
NrnMappingInfo mapinfo
mapping information
int n_memb_func
Definition: init.cpp:440
#define arg
Definition: redef.h:28
#define NULL
Definition: sptree.h:16
int * dparam_semantics
Definition: membfunc.h:56
Compartment mapping information for NrnThread.
size_t size()
number of cells
void clear()
after writing NrnThread to file we remove all previous mapping information, free memory.
Represent main neuron object computed by single thread.
Definition: multicore.h:58
double _t
Definition: multicore.h:59