NEURON
nrncore_utils.cpp
Go to the documentation of this file.
1 #include "nrncore_utils.h"
3 
4 #include "nrnconf.h"
5 #include "nrniv_mf.h"
6 #include <cstdlib>
7 #include "nrndae_c.h"
8 #include "section.h"
9 #include "hocdec.h"
10 #include "nrnsection_mapping.h"
11 #include "vrecitem.h" // for nrnbbcore_vecplay_write
12 #include "parse.hpp"
13 #include <string>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <algorithm>
18 #include <cerrno>
19 #include <filesystem>
20 
21 #include "nrnwrap_dlfcn.h"
22 
23 // RTLD_NODELETE is used with dlopen
24 // if not defined it's safe to define as 0
25 #ifndef RTLD_NODELETE
26 #define RTLD_NODELETE 0
27 #endif
28 
29 extern bool corenrn_direct;
30 extern const char* bbcore_write_version;
31 extern NrnMappingInfo mapinfo;
32 extern void (*nrnthread_v_transfer_)(NrnThread*);
33 extern short* nrn_is_artificial_;
34 
35 
36 // prerequisites for a NEURON model to be transferred to CoreNEURON.
37 void model_ready() {
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  }
55  "NEURON model internal structures for CoreNEURON are out of date. Make sure call to "
56  "finitialize(...)",
57  NULL);
58  }
59 }
60 
61 /** @brief Count number of unique elements in the array.
62  * there is a copy of the vector but we are primarily
63  * using it for small section list vectors.
64  */
65 int count_distinct(double* data, int len) {
66  if (len == 0)
67  return 0;
68  std::vector<double> v;
69  v.assign(data, data + len);
70  std::sort(v.begin(), v.end());
71  return std::unique(v.begin(), v.end()) - v.begin();
72 }
73 
74 
75 /** @brief For BBP use case, we want to write section-segment
76  * mapping to gid_3.dat file. This information will be
77  * provided through neurodamus HOC interface with following
78  * format:
79  * gid : number of non-empty neurons in the cellgroup
80  * name : name of section list (like soma, axon, apic)
81  * nsec : number of sections
82  * sections : list of sections
83  * segments : list of segments
84  * seg_lfp_factors: list of lfp factors
85  */
87  // gid of a cell
88  int gid = *hoc_getarg(1);
89 
90  // name of section list
91  std::string name = std::string(hoc_gargstr(2));
92 
93  // hoc vectors: sections and segments
94  Vect* sec = vector_arg(3);
95  Vect* seg = vector_arg(4);
96  Vect* lfp = ifarg(5) ? vector_arg(5) : new Vect();
97  int electrodes_per_segment = ifarg(6) ? *hoc_getarg(6) : 0;
98 
99  double* sections = vector_vec(sec);
100  double* segments = vector_vec(seg);
101  double* seg_lfp_factors = vector_vec(lfp);
102 
103  int nsec = vector_capacity(sec);
104  int nseg = vector_capacity(seg);
105  int nlfp = vector_capacity(lfp);
106 
107  if (nsec != nseg) {
108  Printf("Error: Section and Segment mapping vectors should have same size!\n");
109  abort();
110  }
111 
112  // number of unique sections
113  nsec = count_distinct(sections, nsec);
114 
115  SecMapping* smap = new SecMapping(nsec, name);
116  smap->sections.assign(sections, sections + nseg);
117  smap->segments.assign(segments, segments + nseg);
118  smap->seglfp_factors.assign(seg_lfp_factors, seg_lfp_factors + nlfp);
119  smap->num_electrodes = electrodes_per_segment;
120 
121  // store mapping information
122  mapinfo.add_sec_mapping(gid, smap);
123 }
124 
125 // This function is related to legacy_index2pointer in CoreNeuron to determine
126 // which values should be transferred from CoreNeuron. Types correspond to the
127 // value to be transferred based on mech_type enum or non-artificial cell
128 // mechanisms.
129 // Limited to pointers to voltage, nt.node_sav_rhs_storage() (fast_imem value) or
130 // data of non-artificial cell mechanisms.
131 // Input double* and NrnThread. Output type and index.
132 // type == 0 means could not determine index.
134  NrnThread& nt,
135  int& type,
136  int& index) {
137  int nnode = nt.end;
138  type = 0;
140  auto const cache_token = nrn_ensure_model_data_are_sorted();
141  type = voltage;
142  // In the CoreNEURON world this is an offset into the voltage array part
143  // of _data
144  index = dh.current_row() - cache_token.thread_cache(nt.id).node_data_offset;
145  return 0;
146  }
148  auto const cache_token = nrn_ensure_model_data_are_sorted();
149  type = i_membrane_; // signifies an index into i_membrane_ array portion of _data
150  index = dh.current_row() - cache_token.thread_cache(nt.id).node_data_offset;
151  return 0;
152  }
153  auto* const pd = static_cast<double*>(dh);
154  for (NrnThreadMembList* tml = nt.tml; tml; tml = tml->next) {
155  if (nrn_is_artificial_[tml->index]) {
156  continue;
157  }
158  if (auto const maybe_index = tml->ml->legacy_index(pd); maybe_index >= 0) {
159  type = tml->index;
160  index = maybe_index;
161  break;
162  }
163  }
164  return type == 0 ? 1 : 0;
165 }
166 
167 
168 #if defined(HAVE_DLFCN_H)
169 
170 extern char* neuron_home;
171 
172 /** Check if coreneuron is loaded into memory */
173 bool is_coreneuron_loaded() {
174  bool is_loaded = false;
175  // check if corenrn_embedded_run symbol can be found
176  void* handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
177  if (handle) {
178  void* fn = dlsym(handle, "corenrn_embedded_run");
179  is_loaded = fn == NULL ? false : true;
180  dlclose(handle);
181  }
182  return is_loaded;
183 }
184 
185 
186 /** Open library with given path and return dlopen handle **/
187 void* get_handle_for_lib(std::filesystem::path const& path) {
188  // On windows path.c_str() is wchar_t*
189  void* handle = dlopen(path.string().c_str(), RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
190  if (!handle) {
191  fputs(dlerror(), stderr);
192  fputs("\n", stderr);
193  hoc_execerror("Could not dlopen CoreNEURON mechanism library : ", path.string().c_str());
194  }
195  return handle;
196 }
197 
198 /** Get CoreNEURON mechanism library */
199 void* get_coreneuron_handle() {
200  // if already loaded into memory, directly return handle
201  if (is_coreneuron_loaded()) {
202  return dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
203  }
204 
205  // record what we tried so we can give a helpful error message
206  std::vector<std::filesystem::path> paths_tried;
207  paths_tried.reserve(3);
208 
209  // env variable get highest preference
210  const char* corenrn_lib = std::getenv("CORENEURONLIB");
211  if (corenrn_lib) {
212  std::filesystem::path const corenrn_lib_path{corenrn_lib};
213  paths_tried.push_back(corenrn_lib_path);
214  if (std::filesystem::exists(corenrn_lib_path)) {
215  return get_handle_for_lib(corenrn_lib_path);
216  }
217  }
218 
219  auto const corenrn_lib_name = std::string{neuron::config::shared_library_prefix}
220  .append("corenrnmech")
221  .append(neuron::config::shared_library_suffix);
222 
223  // first check if coreneuron specific library exist in <arch>
224  // note that we need to get full path especially for OSX
225  {
226  auto const corenrn_lib_path = std::filesystem::current_path() /
227  neuron::config::system_processor / corenrn_lib_name;
228  paths_tried.push_back(corenrn_lib_path);
229  if (std::filesystem::exists(corenrn_lib_path)) {
230  return get_handle_for_lib(corenrn_lib_path);
231  }
232  }
233 
234  // last fallback is minimal library with internal mechanisms
235  // named libcorenrnmech_internal
236  std::filesystem::path corenrn_lib_path{neuron_home};
237  auto const corenrn_internal_lib_name = std::string{neuron::config::shared_library_prefix}
238  .append("corenrnmech_internal")
239  .append(neuron::config::shared_library_suffix);
240 #ifndef MINGW
241  (corenrn_lib_path /= "..") /= "..";
242 #endif
243  (corenrn_lib_path /= "lib") /= corenrn_internal_lib_name;
244  paths_tried.push_back(corenrn_lib_path);
245  if (std::filesystem::exists(corenrn_lib_path)) {
246  return get_handle_for_lib(corenrn_lib_path);
247  }
248  // Nothing worked => error
249  std::ostringstream oss;
250  oss << "Could not find CoreNEURON library, tried:";
251  for (auto const& path: paths_tried) {
252  oss << ' ' << path;
253  }
254  throw std::runtime_error(oss.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 
275 #endif //! HAVE_DLFCN_H
int tree_changed
Definition: cabcode.cpp:51
#define v
Definition: md1redef.h:11
#define sec
Definition: md1redef.h:20
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
double * hoc_getarg(int narg)
Definition: code.cpp:1641
char * hoc_gargstr(int)
IvocVect * vector_arg(int i)
Definition: ivocvect.cpp:265
const char * neuron_home
Definition: hoc_init.cpp:227
IvocVect Vect
Definition: ivocvect.h:134
const char * name
Definition: init.cpp:16
NrnThread * nrn_threads
Definition: multicore.cpp:56
double * vector_vec(IvocVect *v)
Definition: ivocvect.cpp:19
bool cvode_active_
Definition: netcvode.cpp:36
int v_structure_change
Definition: nrnoc_aux.cpp:20
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
int vector_capacity(IvocVect *v)
Definition: ivocvect.cpp:16
int diam_changed
Definition: nrnoc_aux.cpp:21
handle_interface< non_owning_identifier< storage > > handle
Non-owning handle to a Mechanism instance.
Model & model()
Access the global Model instance.
Definition: model_data.hpp:206
neuron::model_sorted_token nrn_ensure_model_data_are_sorted()
Ensure neuron::container::* data are sorted.
Definition: treeset.cpp:2182
@ i_membrane_
@ voltage
short * nrn_is_artificial_
Definition: init.cpp:214
void(* nrnthread_v_transfer_)(NrnThread *)
Definition: fadvance.cpp:139
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 nrn_dblpntr2nrncore(neuron::container::data_handle< double > dh, NrnThread &nt, int &type, int &index)
void model_ready()
const char * bbcore_write_version
Definition: nrncore_io.cpp:25
NrnMappingInfo mapinfo
mapping information
int nrndae_list_is_empty()
Definition: nrndae.cpp:13
int ifarg(int)
Definition: code.cpp:1607
short index
Definition: cabvars.h:11
short type
Definition: cabvars.h:10
static double unique(void *v)
Definition: seclist.cpp:193
#define NULL
Definition: spdefs.h:105
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
int id
Definition: multicore.h:66
int end
Definition: multicore.h:65
struct NrnThreadMembList * next
Definition: multicore.h:34
Section to segment mapping.
int num_electrodes
Number of electrodes per segment.
std::vector< int > segments
list of segments
std::vector< double > seglfp_factors
list of lfp factors associated with each segment
std::vector< int > sections
list sections associated with each segment
container::Node::storage & node_data()
Access the structure containing the data of all Nodes.
Definition: model_data.hpp:24
Field for fast_imem calculation.
Definition: node.hpp:65
bool refers_to(Container const &container) const
Query whether this generic handle points to a value from the Tag field of the given container.
std::size_t current_row() const
Get the current logical row number.
int Printf(const char *fmt, Args... args)
Definition: logger.hpp:18