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