NEURON
nrncore_utils.cpp
Go to the documentation of this file.
1 #include "nrncore_utils.h"
3 
4 #include "nrnconf.h"
5 #include <cstdlib>
6 #include "nrndae_c.h"
7 #include "section.h"
8 #include "hocdec.h"
9 #include "nrnsection_mapping.h"
10 #include "vrecitem.h" // for nrnbbcore_vecplay_write
11 #include "parse.hpp"
12 #include <string>
13 #include <unistd.h>
14 #include <algorithm>
15 #include <cerrno>
16 
17 
18 #ifdef MINGW
19 #define RTLD_NOW 0
20 #define RTLD_GLOBAL 0
21 #define RTLD_NOLOAD 0
22 #define RTLD_NODELETE 0
23 extern "C" {
24 extern void* dlopen_noerr(const char* name, int mode);
25 #define dlopen dlopen_noerr
26 extern void* dlsym(void* handle, const char* name);
27 extern int dlclose(void* handle);
28 extern char* dlerror();
29 }
30 #else
31 #if defined(HAVE_DLFCN_H)
32 #include <dlfcn.h>
33 #endif
34 #endif
35 
36 // RTLD_NODELETE is used with dlopen
37 // if not defined it's safe to define as 0
38 #ifndef RTLD_NODELETE
39 #define RTLD_NODELETE 0
40 #endif
41 
42 extern bool corenrn_direct;
44 extern const char *bbcore_write_version;
45 extern NrnMappingInfo mapinfo;
47 extern short* nrn_is_artificial_;
48 
49 
50 // prerequisites for a NEURON model to be transferred to CoreNEURON.
51 void model_ready() {
52  // Do the model type checks first as some of them prevent the success
53  // of cvode.cache_efficient(1) and the error message associated with
54  // !use_cachevec would be misleading.
55  if (!nrndae_list_is_empty()) {
56  hoc_execerror("CoreNEURON cannot simulate a model that contains extra LinearMechanism or RxD equations", NULL);
57  }
58  if (nrn_threads[0]._ecell_memb_list) {
59  hoc_execerror("CoreNEURON cannot simulate a model that contains the extracellular mechanism", NULL);
60  }
61  if (corenrn_direct) {
62  if (cvode_active_) {
63  hoc_execerror("CoreNEURON can only use fixed step method.", NULL);
64  }
65  }
66 
67  if (!use_cachevec) {
68  hoc_execerror("NEURON model for CoreNEURON requires cvode.cache_efficient(1)", NULL);
69  }
71  hoc_execerror("NEURON model internal structures for CoreNEURON are out of date. Make sure call to finitialize(...) is after cvode.cache_efficient(1))", NULL);
72  }
73 }
74 
75 /** @brief Count number of unique elements in the array.
76  * there is a copy of the vector but we are primarily
77  * using it for small section list vectors.
78  */
79 int count_distinct(double *data, int len) {
80  if( len == 0)
81  return 0;
82  std::vector<double> v;
83  v.assign(data, data + len);
84  std::sort(v.begin(), v.end());
85  return std::unique(v.begin(), v.end()) - v.begin();
86 }
87 
88 
89 /** @brief For BBP use case, we want to write section-segment
90  * mapping to gid_3.dat file. This information will be
91  * provided through neurodamus HOC interface with following
92  * format:
93  * gid : number of non-empty neurons in the cellgroup
94  * name : name of section list (like soma, axon, apic)
95  * nsec : number of sections
96  * sections : list of sections
97  * segments : list of segments
98  */
100 
101  // gid of a cell
102  int gid = *hoc_getarg(1);
103 
104  // name of section list
105  std::string name = std::string(hoc_gargstr(2));
106 
107  // hoc vectors: sections and segments
108  Vect* sec = vector_arg(3);
109  Vect* seg = vector_arg(4);
110 
111  double* sections = vector_vec(sec);
112  double* segments = vector_vec(seg);
113 
114  int nsec = vector_capacity(sec);
115  int nseg = vector_capacity(seg);
116 
117  if( nsec != nseg ) {
118  std::cout << "Error: Section and Segment mapping vectors should have same size!\n";
119  abort();
120  }
121 
122  // number of unique sections
123  nsec = count_distinct(sections, nsec);
124 
125  SecMapping *smap = new SecMapping(nsec, name);
126  smap->sections.assign(sections, sections+nseg);
127  smap->segments.assign(segments, segments+nseg);
128 
129  // store mapping information
130  mapinfo.add_sec_mapping(gid, smap);
131 }
132 
133 /** Check if file with given path exist */
134 bool file_exist(const std::string& path) {
135  std::ifstream f(path.c_str());
136  return f.good();
137 }
138 
139 
140 // This function is related to stdindex2ptr in CoreNeuron to determine which values should
141 // be transferred from CoreNeuron. Types correspond to the value to be transferred based on
142 // mech_type enum or non-artificial cell mechanisms.
143 // Limited to pointers to voltage, nt._nrn_fast_imem->_nrn_sav_rhs (fast_imem value) or
144 // data of non-artificial cell mechanisms.
145 // Requires cache_efficient mode.
146 // Input double* and NrnThread. Output type and index.
147 // type == 0 means could not determine index.
148 extern "C" int nrn_dblpntr2nrncore(double* pd, NrnThread& nt, int& type, int& index) {
150  int nnode = nt.end;
151  type = 0;
152  if (pd >= nt._actual_v && pd < (nt._actual_v + nnode)) {
153  type = voltage; // signifies an index into voltage array portion of _data
154  index = pd - nt._actual_v;
155  } else if (nt._nrn_fast_imem && pd >= nt._nrn_fast_imem->_nrn_sav_rhs && pd < (nt._nrn_fast_imem->_nrn_sav_rhs + nnode)) {
156  type = i_membrane_; // signifies an index into i_membrane_ array portion of _data
157  index = pd - nt._nrn_fast_imem->_nrn_sav_rhs;
158  }else{
159  for (NrnThreadMembList* tml = nt.tml; tml; tml = tml->next) {
160  if (nrn_is_artificial_[tml->index]) { continue; }
161  Memb_list* ml1 = tml->ml;
162  int nn = nrn_prop_param_size_[tml->index] * ml1->nodecount;
163  if (pd >= ml1->data[0] && pd < (ml1->data[0] + nn)) {
164  type = tml->index;
165  index = pd - ml1->data[0];
166  break;
167  }
168  }
169  }
170  return type == 0 ? 1 : 0;
171 }
172 
173 
174 #if defined(HAVE_DLFCN_H)
175 
176 extern int nrn_use_fast_imem;
177 extern char* neuron_home;
178 
179 /** Check if coreneuron is loaded into memory */
180 bool is_coreneuron_loaded() {
181  bool is_loaded = false;
182  // check if corenrn_embedded_run symbol can be found
183  void * handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
184  if (handle) {
185  void* fn = dlsym(handle, "corenrn_embedded_run");
186  is_loaded = fn == NULL ? false : true;
187  dlclose(handle);
188  }
189  return is_loaded;
190 }
191 
192 
193 /** Open library with given path and return dlopen handle **/
194 void* get_handle_for_lib(const char* path) {
195  void* handle = dlopen(path, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
196  if (!handle) {
197  fputs(dlerror(), stderr);
198  fputs("\n", stderr);
199  hoc_execerror("Could not dlopen CoreNEURON mechanism library : ", path);
200  }
201  return handle;
202 }
203 
204 /** Get CoreNEURON mechanism library */
205 void* get_coreneuron_handle() {
206  // if already loaded into memory, directly return handle
207  if (is_coreneuron_loaded()) {
208  return dlopen(NULL, RTLD_NOW|RTLD_GLOBAL);
209  }
210 
211  // env variable get highest preference
212  const char* corenrn_lib = getenv("CORENEURONLIB");
213  if (corenrn_lib && file_exist(corenrn_lib)) {
214  return get_handle_for_lib(corenrn_lib);
215  }
216 
217  // name of coreneuron library based on platform
218 #if defined(MINGW)
219  std::string corenrn_mechlib_name("libcorenrnmech.dll");
220 #elif defined(DARWIN)
221  std::string corenrn_mechlib_name("libcorenrnmech.dylib");
222 #else
223  std::string corenrn_mechlib_name("libcorenrnmech.so");
224 #endif
225 
226  // first check if coreneuron specific library exist in <arch>/.libs
227  // note that we need to get full path especially for OSX
228  char pwd[FILENAME_MAX];
229  if (getcwd(pwd, FILENAME_MAX) == NULL) {
230  hoc_execerror("getcwd failed:", strerror(errno));
231  }
232  std::stringstream s_path;
233  s_path << pwd << "/" << NRNHOSTCPU << "/" << corenrn_mechlib_name;
234  std::string path = s_path.str();
235 
236  if (file_exist(path)) {
237  return get_handle_for_lib(path.c_str());
238  }
239 
240  // last fallback is minimal library with internal mechanisms
241  s_path.str("");
242 #if defined(MINGW)
243  s_path << neuron_home << "/lib/" << corenrn_mechlib_name;
244 #else
245  s_path << neuron_home << "/../../lib/" << corenrn_mechlib_name;
246 #endif
247  path = s_path.str();
248 
249  // if this last path doesn't exist then it's an error
250  if (!file_exist(path)) {
251  hoc_execerror("Could not find CoreNEURON library", NULL);
252  }
253 
254  return get_handle_for_lib(path.c_str());
255 }
256 
257 /** Check if neuron & coreneuron are compatible */
258 void check_coreneuron_compatibility(void* handle) {
259  // get handle to function in coreneuron
260  void* cn_version_sym = dlsym(handle, "corenrn_version");
261  if (!cn_version_sym) {
262  hoc_execerror("Could not get symbol corenrn_version from CoreNEURON", NULL);
263  }
264  // call coreneuron function and get version string
265  const char* cn_bbcore_read_version = (*(const char*(*)())cn_version_sym)();
266 
267  // make sure neuron and coreneuron version are same; otherwise throw an error
268  if (strcmp(bbcore_write_version, cn_bbcore_read_version) != 0) {
269  std::stringstream s_path;
270  s_path << bbcore_write_version << " vs " << cn_bbcore_read_version;
271  hoc_execerror("Incompatible NEURON and CoreNEURON versions :", s_path.str().c_str());
272  }
273 
274  // Make sure legacy vs modern units are consistent.
275  // Would be nice to check in coreneuron set_globals but that would abort
276  // if inconsistent.
277  void* cn_nrnunit_use_legacy_sym = dlsym(handle, "corenrn_units_use_legacy");
278  if (!cn_nrnunit_use_legacy_sym) {
279  hoc_execerror("Could not get symbol corenrn_units_use_legacy from CoreNEURON", NULL);
280  }
281  bool cn_nrnunit_use_legacy = (*(bool(*)())cn_nrnunit_use_legacy_sym)();
282  if (cn_nrnunit_use_legacy != (_nrnunit_use_legacy_ == 1)) {
283  hoc_execerror("nrnunit_use_legacy() inconsistent with CORENRN_ENABLE_LEGACY_UNITS", NULL);
284  }
285 }
286 
287 #endif //!HAVE_DLFCN_H
288 
int nrn_use_fast_imem
Definition: fadvance.cpp:162
#define data
Definition: rbtqueue.cpp:49
int count_distinct(double *data, int len)
Count number of unique elements in the array.
Section to segment mapping.
#define assert(ex)
Definition: hocassrt.h:26
short type
Definition: cabvars.h:10
#define Vect
Definition: ivocvect.h:14
struct NrnThreadMembList * next
Definition: multicore.h:34
const char * dlerror(void)
Definition: osxdlfcn.cpp:174
int nrn_dblpntr2nrncore(double *pd, NrnThread &nt, int &type, int &index)
int nrndae_list_is_empty()
Definition: nrndae.cpp:23
void
Represent main neuron object computed by single thread.
Definition: multicore.h:58
short * nrn_is_artificial_
Definition: init.cpp:231
int _nrnunit_use_legacy_
Definition: hoc_init.cpp:273
int diam_changed
Definition: cabcode.cpp:23
#define v
Definition: md1redef.h:4
double ** data
Definition: nrnoc_ml.h:14
int dlclose(void *handle)
Definition: osxdlfcn.cpp:178
NrnMappingInfo mapinfo
mapping information
_nrn_Fast_Imem * _nrn_fast_imem
Definition: multicore.h:82
int v_structure_change
Definition: cvodestb.cpp:99
j< sec-> nnode
Definition: treeset.cpp:905
#define RTLD_NOW
Definition: osxdlfcn.h:61
void nrnbbcore_register_mapping()
For BBP use case, we want to write section-segment mapping to gid_3.dat file.
bool corenrn_direct
int nodecount
Definition: nrnoc_ml.h:18
NrnThread * nrn_threads
Definition: multicore.cpp:45
double * _nrn_sav_rhs
Definition: multicore.h:46
int use_cachevec
Definition: treeset.cpp:61
void model_ready()
void hoc_execerror(const char *, const char *)
Definition: hoc.cpp:741
errno
Definition: system.cpp:98
char * getenv(const char *s)
Definition: macprt.cpp:67
const char * neuron_home
Definition: hoc_init.cpp:268
int tree_changed
Definition: cabcode.cpp:19
Compartment mapping information for NrnThread.
void * dlopen(const char *path, int mode)
Definition: osxdlfcn.cpp:85
char * name
Definition: init.cpp:16
int * nrn_prop_param_size_
Definition: init.cpp:178
void add_sec_mapping(int gid, SecMapping *s)
add section mapping information for given gid if cell is not peviously added, create new cell mapping...
int end
Definition: multicore.h:65
bool file_exist(const std::string &path)
Check if file with given path exist.
Vect * vector_arg(int i)
Definition: ivocvect.cpp:332
int vector_capacity(Vect *v)
Definition: ivocvect.cpp:268
static double unique(void *v)
Definition: seclist.cpp:206
double * vector_vec(Vect *v)
Definition: ivocvect.cpp:271
sec
Definition: solve.cpp:885
NrnThreadMembList * tml
Definition: multicore.h:62
void * dlsym(void *handle, const char *symbol)
Definition: osxdlfcn.cpp:193
void(* nrnthread_v_transfer_)(NrnThread *)
Definition: fadvance.cpp:148
#define RTLD_NODELETE
return NULL
Definition: cabcode.cpp:461
#define RTLD_GLOBAL
Definition: osxdlfcn.h:63
int cvode_active_
Definition: fadvance.cpp:158
std::vector< int > segments
list of segments
double * _actual_v
Definition: multicore.h:74
short index
Definition: cabvars.h:11
const char * bbcore_write_version
Definition: nrncore_io.cpp:25
std::vector< int > sections
list sections associated with each segment