NEURON
report_handler.cpp
Go to the documentation of this file.
1 /*
2 # =============================================================================
3 # Copyright (c) 2016 - 2021 Blue Brain Project/EPFL
4 #
5 # See top-level LICENSE file for details.
6 # =============================================================================
7 */
8 
9 #include <sstream>
10 #include <unordered_set>
11 #include "report_handler.hpp"
16 
17 namespace coreneuron {
18 #ifdef ENABLE_SONATA_REPORTS
19 
20 void ReportHandler::register_section_report(const NrnThread& nt,
21  const ReportConfiguration& config,
22  const VarsToReport& vars_to_report,
23  bool is_soma_target) {
24  if (nrnmpi_myid == 0) {
25  std::cerr << "[WARNING] : Format '" << config.format << "' in report '"
26  << config.output_path << "' not supported.\n";
27  }
28 }
29 void ReportHandler::register_custom_report(const NrnThread& nt,
30  const ReportConfiguration& config,
31  const VarsToReport& vars_to_report) {
32  if (nrnmpi_myid == 0) {
33  std::cerr << "[WARNING] : Format '" << config.format << "' in report '"
34  << config.output_path << "' not supported.\n";
35  }
36 }
37 
38 /**
39  * @brief Map each segment_id to a single node index in the given Memb_list.
40  *
41  * Lazily populates the map only if empty. Aborts if a segment_id appears more than once,
42  * which is invalid for compartment reports expecting exactly one variable per segment.
43  * Differs from the vector variant by enforcing uniqueness and aborting on duplicates.
44  */
45 static void fill_segment_id_2_node_id(Memb_list* ml,
46  std::unordered_map<int, int>& segment_id_2_node_id) {
47  nrn_assert(ml && "Memb_list is a nullptr!");
48 
49  // do not fill if not empty. We already did this. Be lazy
50  if (!segment_id_2_node_id.empty()) {
51  return;
52  }
53 
54  for (int j = 0; j < ml->nodecount; j++) {
55  const int segment_id = ml->nodeindices[j];
56  auto result = segment_id_2_node_id.insert({segment_id, j});
57  if (!result.second) {
58  std::cerr << "Found duplicate in nodeindices: " << segment_id << " index: " << j
59  << std::endl;
60  std::cerr << "The nodeindices (size: " << ml->nodecount << ") are:\n[";
61  for (int j = 0; j < ml->nodecount; j++) {
62  std::cerr << ml->nodeindices[j] << ", ";
63  }
64  std::cerr << "]\n";
65  std::cerr << "Probably you asked for a compartment report for a synapse variable and "
66  "there are many synapses attached to the soma.\nThis is not allowed since "
67  "compartment reports require 1 and only 1 variable on every segment.\n";
68  nrn_abort(1);
69  }
70  }
71 }
72 
73 /**
74  * @brief Map each segment_id to all corresponding node indices in the given Memb_list.
75  *
76  * Lazily populates the map only if empty. Allows multiple node indices per segment_id
77  * and stores them in a vector. Differs from the single-index variant by permitting
78  * duplicates and not performing any error checks.
79  */
80 static void fill_segment_id_2_node_id(
81  Memb_list* ml,
82  std::unordered_map<int, std::vector<int>>& segment_id_2_node_id) {
83  nrn_assert(ml && "Memb_list is a nullptr!");
84 
85  // do not fill if not empty. We already did this. Be lazy
86  if (!segment_id_2_node_id.empty()) {
87  return;
88  }
89 
90  for (int j = 0; j < ml->nodecount; j++) {
91  const int segment_id = ml->nodeindices[j];
92  segment_id_2_node_id[segment_id].push_back(j);
93  }
94 }
95 
96 /**
97  * @brief Retrieve a pointer to a single mechanism/state variable for a specific segment.
98  *
99  * For "i_membrane" and "v", returns a direct pointer to the relevant thread array.
100  * Otherwise, looks up the mechanism in the thread, maps the segment to its node,
101  * and retrieves the variable pointer. Aborts if the mechanism or variable is missing.
102  * Differs from get_vars() in that it returns a single variable pointer and performs
103  * strict error handling with aborts on failure.
104  */
105 static double* get_one_var(const NrnThread& nt,
106  const int mech_id,
107  const std::string& var_name,
108  const std::string& mech_name,
109  const int segment_id,
110  std::unordered_map<int, int> segment_id_2_node_id,
111  int gid) {
112  if (mech_name == "i_membrane") {
113  return nt.nrn_fast_imem->nrn_sav_rhs + segment_id;
114  }
115  if (mech_name == "v") {
116  return nt._actual_v + segment_id;
117  }
118 
119  if (mech_id < 0) {
120  std::cerr << "Mechanism (" << mech_id << ", " << var_name << ", " << mech_name
121  << ") not found\n";
122  std::cerr << "Negative mech_id\n";
123  nrn_abort(1);
124  }
125 
126  Memb_list* ml = nt._ml_list[mech_id];
127 
128  if (!ml) {
129  std::cerr << "Mechanism (" << mech_id << ", " << var_name << ", " << mech_name
130  << ") not found\n";
131  std::cerr << "NULL Memb_list\n";
132  nrn_abort(1);
133  }
134 
135  // lazy cache
136  fill_segment_id_2_node_id(ml, segment_id_2_node_id);
137 
138  const auto it = segment_id_2_node_id.find(segment_id);
139  // there is no mechanism at this segment. Notice that segment_id_2_node_id
140  // is a segment_id -> node_id that changes with mech_id and gid
141  if (it == segment_id_2_node_id.end()) {
142  std::cerr << "Mechanism (" << mech_id << ", " << var_name << ", " << mech_name
143  << ") not found at gid: " << gid << " segment: " << segment_id << std::endl;
144  std::cerr << "The nodeindices (size: " << ml->nodecount << ") are:\n[";
145  for (int j = 0; j < ml->nodecount; j++) {
146  std::cerr << ml->nodeindices[j] << ", ";
147  }
148  std::cerr << "]\n";
149  nrn_abort(1);
150  }
151 
152  const int node_id = it->second;
153 
154 
155  const auto ans = get_var_location_from_var_name(mech_id, mech_name, var_name, ml, node_id);
156  if (ans == nullptr) {
157  std::cerr
158  << "get_var_location_from_var_name failed to retrieve a valid pointer for mechanism ("
159  << mech_id << ", " << var_name << ", " << mech_name << ") at gid: " << gid
160  << " segment: " << segment_id << std::endl;
161  nrn_abort(1);
162  }
163  return ans;
164 }
165 
166 /**
167  * @brief Retrieve pointers to a mechanism/state variable for all nodes in a segment.
168  *
169  * For "i_membrane" and "v", returns a vector containing a single direct pointer.
170  * Otherwise, looks up the mechanism in the thread, maps the segment to its nodes,
171  * and retrieves pointers for each node. Returns an empty vector on any failure.
172  * Differs from get_one_var() in that it returns multiple pointers (vector) and
173  * fails gracefully without aborting.
174  */
175 static std::vector<double*> get_vars(
176  const NrnThread& nt,
177  const int mech_id,
178  const std::string& var_name,
179  const std::string& mech_name,
180  const int segment_id,
181  std::unordered_map<int, std::vector<int>>& segment_id_2_node_ids) {
182  if (mech_name == "i_membrane") {
183  return {nt.nrn_fast_imem->nrn_sav_rhs + segment_id};
184  }
185  if (mech_name == "v") {
186  return {nt._actual_v + segment_id};
187  }
188 
189  if (mech_id < 0) {
190  return {};
191  }
192 
193  Memb_list* ml = nt._ml_list[mech_id];
194 
195  if (!ml) {
196  return {};
197  }
198 
199  // lazy cache
200  fill_segment_id_2_node_id(ml, segment_id_2_node_ids);
201 
202  const auto it = segment_id_2_node_ids.find(segment_id);
203  // there is no mechanism at this segment. Notice that segment_id_2_node_id
204  // is a segment_id -> node_id that changes with mech_id and gid
205  if (it == segment_id_2_node_ids.end()) {
206  return {};
207  }
208 
209  const std::vector<int>& node_ids = it->second;
210 
211  std::vector<double*> ans;
212  for (auto node_id: node_ids) {
213  const auto var = get_var_location_from_var_name(mech_id, mech_name, var_name, ml, node_id);
214  if (var) {
215  ans.push_back(var);
216  }
217  }
218  return ans;
219 }
220 
221 /**
222  * @brief Compute a scaling factor for a mechanism variable.
223  *
224  * Inverts current for "IClamp" and "SEClamp". For area scaling (except "i_membrane"),
225  * uses the segment area in µm² divided by 100. Returns 1.0 otherwise.
226  */
227 static double get_scaling_factor(const std::string& mech_name,
228  Scaling scaling,
229  const NrnThread& nt,
230  const int segment_id) {
231  // Scaling factors for specific mechanisms
232  if (mech_name == "IClamp" || mech_name == "SEClamp") {
233  return -1.0; // Invert current for these mechanisms
234  }
235 
236  if (mech_name != "i_membrane" && scaling == Scaling::Area) {
237  return (*(nt._actual_area + segment_id)) / 100.0;
238  }
239 
240  return 1.0; // Default scaling factor
241 }
242 
243 static void append_sections_to_to_report_auto_variable(
244  const std::shared_ptr<SecMapping>& sections,
245  std::vector<VarWithMapping>& to_report,
246  const ReportConfiguration& report_conf,
247  const NrnThread& nt,
248  std::unordered_map<int, int>& segment_id_2_node_id,
249  int gid) {
250  nrn_assert(report_conf.mech_ids.size() == 1);
251  nrn_assert(report_conf.var_names.size() == 1);
252  nrn_assert(report_conf.mech_names.size() == 1);
253 
254  const auto& mech_id = report_conf.mech_ids[0];
255  const auto& var_name = report_conf.var_names[0];
256  const auto& mech_name = report_conf.mech_names[0];
257  for (const auto& section: sections->secmap) {
258  // compartment_id
259  int section_id = section.first;
260  const auto& segment_ids = section.second;
261 
262  // get all compartment values (otherwise, just middle point)
263  if (report_conf.compartments == Compartments::All) {
264  for (const auto& segment_id: segment_ids) {
265  double* variable = get_one_var(
266  nt, mech_id, var_name, mech_name, segment_id, segment_id_2_node_id, gid);
267  to_report.emplace_back(VarWithMapping(section_id, variable));
268  }
269  } else if (report_conf.compartments == Compartments::Center) {
270  nrn_assert(segment_ids.size() % 2 &&
271  "Section with an even number of compartments. I cannot pick the middle one. "
272  "This was not expected");
273  // corresponding voltage in coreneuron voltage array
274  const auto segment_id = segment_ids[segment_ids.size() / 2];
275  double* variable = get_one_var(
276  nt, mech_id, var_name, mech_name, segment_id, segment_id_2_node_id, gid);
277  to_report.emplace_back(VarWithMapping(section_id, variable));
278  } else {
279  std::cerr << "[ERROR] Invalid compartments type: "
280  << to_string(report_conf.compartments) << " in report: " << report_conf.name
281  << std::endl;
282  nrn_abort(1);
283  }
284  }
285 }
286 
287 
288 // fill to_report with (int section_id, double* variable)
289 static void append_sections_to_to_report(const std::shared_ptr<SecMapping>& sections,
290  std::vector<VarWithMapping>& to_report,
291  double* report_variable,
292  const ReportConfiguration& report_conf) {
293  for (const auto& section: sections->secmap) {
294  // compartment_id
295  int section_id = section.first;
296  const auto& segment_ids = section.second;
297 
298  // get all compartment values (otherwise, just middle point)
299  if (report_conf.compartments == Compartments::All) {
300  for (const auto& segment_id: segment_ids) {
301  // corresponding voltage in coreneuron voltage array
302  double* variable = report_variable + segment_id;
303  to_report.emplace_back(VarWithMapping(section_id, variable));
304  }
305  } else if (report_conf.compartments == Compartments::Center) {
306  nrn_assert(segment_ids.size() % 2 &&
307  "Section with an even number of compartments. I cannot pick the middle one. "
308  "This was not expected");
309  // corresponding voltage in coreneuron voltage array
310  const auto segment_id = segment_ids[segment_ids.size() / 2];
311  double* variable = report_variable + segment_id;
312  to_report.emplace_back(VarWithMapping(section_id, variable));
313  } else {
314  std::cerr << "[ERROR] Invalid compartments type: "
315  << to_string(report_conf.compartments) << " in report: " << report_conf.name
316  << std::endl;
317  nrn_abort(1);
318  }
319  }
320 }
321 
322 
323 /// @brief create an vector of ids in target_gids that are on this thread
324 static std::vector<int> get_intersection_ids(const NrnThread& nt, std::vector<int>& target_gids) {
325  std::unordered_set<int> thread_gids;
326  for (int i = 0; i < nt.ncell; i++) {
327  thread_gids.insert(nt.presyns[i].gid_);
328  }
329  std::vector<int> intersection_ids;
330  intersection_ids.reserve(target_gids.size());
331 
332  for (auto i = 0; i < target_gids.size(); ++i) {
333  const auto gid = target_gids[i];
334  if (thread_gids.find(gid) != thread_gids.end()) {
335  intersection_ids.push_back(i);
336  }
337  }
338 
339  intersection_ids.shrink_to_fit();
340  return intersection_ids;
341 }
342 
343 /// loop over the points of a compartment set and to return VarsToReport
344 static VarsToReport get_compartment_set_vars_to_report(const NrnThread& nt,
345  const std::vector<int>& intersection_ids,
346  const ReportConfiguration& report) {
347  nrn_assert(report.mech_ids.size() == 1);
348  nrn_assert(report.var_names.size() == 1);
349  nrn_assert(report.mech_names.size() == 1);
350  nrn_assert(report.sections == SectionType::Invalid &&
351  "[ERROR] : Compartment report `sections` should be Invalid");
352  nrn_assert(report.compartments == Compartments::Invalid &&
353  "[ERROR] : Compartment report `compartments` should be Invalid");
354 
355  nrn_assert(report.target.size() == report.point_compartment_ids.size());
356  nrn_assert(report.target.size() == report.point_section_ids.size());
357 
358  const auto& mech_id = report.mech_ids[0];
359  const auto& var_name = report.var_names[0];
360  const auto& mech_name = report.mech_names[0];
361 
362  VarsToReport vars_to_report;
363  const auto* mapinfo = static_cast<NrnThreadMappingInfo*>(nt.mapping);
364  if (!mapinfo) {
365  std::cerr << "[ERROR] : Compartment report mapping information is missing for a Cell group "
366  << nt.ncell << '\n';
367  nrn_abort(1);
368  }
369 
370  int old_gid = -1;
371  std::unordered_map<int, int> segment_id_2_node_id;
372  for (const auto& intersection_id: intersection_ids) {
373  const auto gid = report.target[intersection_id];
374  const auto section_id = report.point_section_ids[intersection_id];
375  auto compartment_id = report.point_compartment_ids[intersection_id];
376  if (nt._permute) {
377  node_permute(&compartment_id, 1, nt._permute);
378  }
379 
380  if (old_gid != gid) {
381  // clear cache for new gid. We know they are strictly ordered
382  segment_id_2_node_id.clear();
383  }
384 
385  double* variable = get_one_var(
386  nt, mech_id, var_name, mech_name, compartment_id, segment_id_2_node_id, gid);
387  // automatically calls an empty constructor if key is not present
388  vars_to_report[gid].emplace_back(section_id, variable);
389  old_gid = gid;
390  }
391  return vars_to_report;
392 }
393 
394 
395 static VarsToReport get_section_vars_to_report(const NrnThread& nt,
396  const std::vector<int>& intersection_ids,
397  const ReportConfiguration& report) {
398  nrn_assert(report.sections != SectionType::Invalid &&
399  "[ERROR] : Compartment report `sections` should not be Invalid");
400  nrn_assert(report.compartments != Compartments::Invalid &&
401  "[ERROR] : Compartment report `compartments` should not be Invalid");
402  VarsToReport vars_to_report;
403  const auto* mapinfo = static_cast<NrnThreadMappingInfo*>(nt.mapping);
404  if (!mapinfo) {
405  std::cerr << "[ERROR] : Compartment report mapping information is missing for a Cell group "
406  << nt.ncell << '\n';
407  nrn_abort(1);
408  }
409 
410  for (const auto& intersection_id: intersection_ids) {
411  const auto gid = report.target[intersection_id];
412  std::unordered_map<int, int> segment_id_2_node_id;
413  const auto& cell_mapping = mapinfo->get_cell_mapping(gid);
414  if (cell_mapping == nullptr) {
415  std::cerr << "[ERROR] : Compartment report mapping information is missing for gid "
416  << gid << '\n';
417  nrn_abort(1);
418  }
419  std::vector<VarWithMapping> to_report;
420  to_report.reserve(cell_mapping->size());
421 
422  if (report.sections == SectionType::All) {
423  const auto& section_mapping = cell_mapping->sec_mappings;
424  for (const auto& sections: section_mapping) {
425  append_sections_to_to_report_auto_variable(
426  sections, to_report, report, nt, segment_id_2_node_id, gid);
427  }
428  } else {
429  /** get section list mapping for the type, if available */
430  if (cell_mapping->get_seclist_section_count(report.sections) > 0) {
431  const auto& sections = cell_mapping->get_seclist_mapping(report.sections);
432  append_sections_to_to_report_auto_variable(
433  sections, to_report, report, nt, segment_id_2_node_id, gid);
434  }
435  }
436  to_report.shrink_to_fit();
437  vars_to_report[gid] = to_report;
438  }
439  return vars_to_report;
440 }
441 
442 
443 static VarsToReport get_summation_vars_to_report(const NrnThread& nt,
444  const std::vector<int>& intersection_ids,
445  const ReportConfiguration& report) {
446  VarsToReport vars_to_report;
447  const auto* mapinfo = static_cast<NrnThreadMappingInfo*>(nt.mapping);
448  auto& summation_report = nt.summation_report_handler_->summation_reports_[report.output_path];
449 
450  if (!mapinfo) {
451  std::cerr << "[ERROR] : Compartment report mapping information is missing for a Cell group "
452  << nt.ncell << '\n';
453  nrn_abort(1);
454  }
455 
456  for (const auto& intersection_id: intersection_ids) {
457  const auto gid = report.target[intersection_id];
458 
459  const auto& cell_mapping = mapinfo->get_cell_mapping(gid);
460  if (cell_mapping == nullptr) {
461  std::cerr << "[ERROR] : Summation report mapping information is missing for gid " << gid
462  << '\n';
463  nrn_abort(1);
464  }
465 
466  // In case we need convertion of units
467  for (auto i = 0; i < report.mech_ids.size(); ++i) {
468  const auto& mech_id = report.mech_ids[i];
469  const auto& var_name = report.var_names[i];
470  const auto& mech_name = report.mech_names[i];
471  std::unordered_map<int, std::vector<int>> segment_id_2_node_ids;
472 
473  const auto& section_mapping = cell_mapping->sec_mappings;
474  for (const auto& sections: section_mapping) {
475  for (auto& section: sections->secmap) {
476  int section_id = section.first;
477  auto& segment_ids = section.second;
478 
479  for (const auto& segment_id: segment_ids) {
480  double scaling_factor =
481  get_scaling_factor(mech_name, report.scaling, nt, segment_id);
482  // I know that comparing a double to 0.0 is not the best practice,
483  // but they told me to never round this number and to follow how
484  // it is done in neurodamus + neuron. In any case it is innoquous,
485  // it is just to save some computational time. (Katta)
486  if (scaling_factor == 0.0) {
487  continue;
488  }
489 
490  const std::vector<double*> var_ptrs = get_vars(
491  nt, mech_id, var_name, mech_name, segment_id, segment_id_2_node_ids);
492  for (auto var_ptr: var_ptrs) {
493  summation_report.currents_[segment_id].push_back(
494  std::make_pair(var_ptr, scaling_factor));
495  }
496  }
497  }
498  }
499  }
500 
501  std::vector<VarWithMapping> to_report;
502  to_report.reserve(cell_mapping->size());
503  summation_report.summation_.resize(nt.end);
504  double* report_variable = summation_report.summation_.data();
505  nrn_assert(report.sections != SectionType::Invalid &&
506  "ReportHandler::get_summation_vars_to_report: sections should not be Invalid");
507 
508  if (report.sections != SectionType::All) {
509  if (cell_mapping->get_seclist_section_count(report.sections) > 0) {
510  const auto& sections = cell_mapping->get_seclist_mapping(report.sections);
511  append_sections_to_to_report(sections, to_report, report_variable, report);
512  }
513  }
514 
515  const auto& section_mapping = cell_mapping->sec_mappings;
516  for (const auto& sections: section_mapping) {
517  for (auto& section: sections->secmap) {
518  // compartment_id
519  int section_id = section.first;
520  auto& segment_ids = section.second;
521  for (const auto& segment_id: segment_ids) {
522  if (report.sections == SectionType::All) {
523  double* variable = report_variable + segment_id;
524  to_report.emplace_back(VarWithMapping(section_id, variable));
525  } else if (report.sections == SectionType::Soma) {
526  summation_report.gid_segments_[gid].push_back(segment_id);
527  }
528  }
529  }
530  }
531  vars_to_report[gid] = to_report;
532  }
533 
534  return vars_to_report;
535 }
536 
537 static VarsToReport get_synapse_vars_to_report(const NrnThread& nt,
538  const std::vector<int>& intersection_ids,
539  const ReportConfiguration& report) {
540  nrn_assert(report.mech_ids.size() == 1);
541  nrn_assert(report.var_names.size() == 1);
542  nrn_assert(report.mech_names.size() == 1);
543  const auto& mech_id = report.mech_ids[0];
544  const auto& mech_name = report.mech_names[0];
545  const auto& var_name = report.var_names[0];
546  VarsToReport vars_to_report;
547 
548  const auto* mapinfo = static_cast<NrnThreadMappingInfo*>(nt.mapping);
549  for (const auto& intersection_id: intersection_ids) {
550  const auto gid = report.target[intersection_id];
551 
552  const auto& cell_mapping = mapinfo->get_cell_mapping(gid);
553  if (cell_mapping == nullptr) {
554  std::cerr << "[ERROR] : Synapse report mapping information is missing for gid " << gid
555  << '\n';
556  nrn_abort(1);
557  }
558 
559 
560  for (auto i = 0; i < report.mech_ids.size(); ++i) {
561  std::unordered_map<int, std::vector<int>> segment_id_2_node_ids;
562 
563  const auto& section_mapping = cell_mapping->sec_mappings;
564  for (const auto& sections: section_mapping) {
565  for (auto& section: sections->secmap) {
566  int section_id = section.first;
567  auto& segment_ids = section.second;
568  for (const auto& segment_id: segment_ids) {
569  const std::vector<double*> selected_vars = get_vars(nt,
570  mech_id,
571  "selected_for_report",
572  mech_name,
573  segment_id,
574  segment_id_2_node_ids);
575  const std::vector<double*> var_ptrs = get_vars(
576  nt, mech_id, var_name, mech_name, segment_id, segment_id_2_node_ids);
577  const std::vector<double*> synapseIDs = get_vars(
578  nt, mech_id, "synapseID", mech_name, segment_id, segment_id_2_node_ids);
579 
580  nrn_assert(var_ptrs.size() == synapseIDs.size());
581  nrn_assert(selected_vars.empty() ||
582  selected_vars.size() == var_ptrs.size());
583 
584  for (auto i = 0; i < var_ptrs.size(); ++i) {
585  if (selected_vars.empty() || static_cast<bool>(selected_vars[i])) {
586  vars_to_report[gid].push_back(
587  {static_cast<int>(*synapseIDs[i]), var_ptrs[i]});
588  }
589  }
590  }
591  }
592  }
593  }
594  }
595  return vars_to_report;
596 }
597 
598 static VarsToReport get_lfp_vars_to_report(const NrnThread& nt,
599  const std::vector<int>& intersection_ids,
600  ReportConfiguration& report,
601  double* report_variable) {
602  const auto* mapinfo = static_cast<NrnThreadMappingInfo*>(nt.mapping);
603  if (!mapinfo) {
604  std::cerr << "[ERROR] : LFP report mapping information is missing for a Cell group "
605  << nt.ncell << '\n';
606  nrn_abort(1);
607  }
608  auto& summation_report = nt.summation_report_handler_->summation_reports_[report.output_path];
609  VarsToReport vars_to_report;
610  std::size_t offset_lfp = 0;
611  for (const auto& intersection_id: intersection_ids) {
612  const auto gid = report.target[intersection_id];
613  const auto& cell_mapping = mapinfo->get_cell_mapping(gid);
614  if (cell_mapping == nullptr) {
615  std::cerr << "[ERROR] : LFP report mapping information is missing for gid " << gid
616  << '\n';
617  nrn_abort(1);
618  }
619  std::vector<VarWithMapping> to_report;
620  int num_electrodes = cell_mapping->num_electrodes();
621  for (int electrode_id = 0; electrode_id < num_electrodes; electrode_id++) {
622  to_report.emplace_back(VarWithMapping(electrode_id, report_variable + offset_lfp));
623  offset_lfp++;
624  }
625  if (!to_report.empty()) {
626  vars_to_report[gid] = to_report;
627  }
628  }
629  return vars_to_report;
630 }
631 
632 
633 #endif
634 
635 
637  double dt,
638  double tstop,
639  double delay) {
640 #ifdef ENABLE_SONATA_REPORTS
641  if (report_config.start < t) {
642  report_config.start = t;
643  }
644  report_config.stop = std::min(report_config.stop, tstop);
645 
646  for (const auto& mech: report_config.mech_names) {
647  report_config.mech_ids.emplace_back(nrn_get_mechtype(mech.data()));
648  }
649  if (report_config.type == ReportType::Synapse && report_config.mech_ids.empty()) {
650  std::cerr << "[ERROR] Synapse report mechanism to report: " << report_config.mech_names[0]
651  << " is not mapped in this simulation, cannot report on it \n";
652  nrn_abort(1);
653  }
654  for (int ith = 0; ith < nrn_nthread; ++ith) {
655  NrnThread& nt = nrn_threads[ith];
656 
657  if (!nt.ncell) {
658  continue;
659  }
660  auto* mapinfo = static_cast<NrnThreadMappingInfo*>(nt.mapping);
661 
662  const std::vector<int> intersection_ids = get_intersection_ids(nt, report_config.target);
663  VarsToReport vars_to_report;
664  const bool is_soma_target = report_config.sections == SectionType::Soma;
665  switch (report_config.type) {
667  vars_to_report = get_section_vars_to_report(nt, intersection_ids, report_config);
668  register_section_report(nt, report_config, vars_to_report, is_soma_target);
669  break;
670  }
672  vars_to_report =
673  get_compartment_set_vars_to_report(nt, intersection_ids, report_config);
674  register_section_report(nt, report_config, vars_to_report, is_soma_target);
675  break;
676  }
677  case ReportType::Summation: {
678  vars_to_report = get_summation_vars_to_report(nt, intersection_ids, report_config);
679  register_custom_report(nt, report_config, vars_to_report);
680  break;
681  }
682  case ReportType::LFP: {
683  mapinfo->prepare_lfp();
684  vars_to_report =
685  get_lfp_vars_to_report(nt, intersection_ids, report_config, mapinfo->_lfp.data());
686  register_section_report(nt, report_config, vars_to_report, is_soma_target);
687  break;
688  }
689  case ReportType::Synapse: {
690  vars_to_report = get_synapse_vars_to_report(nt, intersection_ids, report_config);
691  register_custom_report(nt, report_config, vars_to_report);
692  break;
693  }
694  default: {
695  std::cerr << "[ERROR] Unknown report type: " << to_string(report_config.type) << '\n';
696  nrn_abort(1);
697  }
698  }
699 
700  if (!vars_to_report.empty()) {
701  auto report_event = std::make_unique<ReportEvent>(dt,
702  t,
703  vars_to_report,
704  report_config.output_path.data(),
705  report_config.report_dt,
706  report_config.type);
707  report_event->send(t, net_cvode_instance, &nt);
708  m_report_events.push_back(std::move(report_event));
709  }
710  }
711 
712 #else
713  if (nrnmpi_myid == 0) {
714  std::cerr << "[WARNING] : Reporting is disabled. Please recompile with libsonata.\n";
715  }
716 #endif
717 }
718 } // Namespace coreneuron
virtual void create_report(ReportConfiguration &config, double dt, double tstop, double delay)
#define i
Definition: md1redef.h:19
double var(InputIterator begin, InputIterator end)
Definition: ivocvect.h:108
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
THIS FILE IS AUTO GENERATED DONT MODIFY IT.
NrnThread * nrn_threads
Definition: multicore.cpp:56
void nrn_abort(int errcode)
Definition: utils.cpp:13
int nrn_nthread
Definition: multicore.cpp:55
double * get_var_location_from_var_name(int mech_id, const std::string_view mech_name, const std::string_view variable_name, Memb_list *ml, int node_index)
int nrn_get_mechtype(const char *name)
Get mechanism type by the mechanism name.
Definition: mk_mech.cpp:145
std::string to_string(EnumT e, const std::array< std::pair< EnumT, std::string_view >, N > &mapping, const std::string_view enum_name)
Converts an enum value to its corresponding string representation.
Definition: nrnreport.hpp:102
NetCvode * net_cvode_instance
Definition: netcvode.cpp:35
#define nrn_assert(x)
assert()-like macro, independent of NDEBUG status
Definition: nrn_assert.h:33
NrnMappingInfo mapinfo
mapping information
size_t j
A view into a set of mechanism instances.
Definition: nrnoc_ml.h:34
int nodecount
Definition: nrnoc_ml.h:78
int * nodeindices
Definition: nrnoc_ml.h:74
CellMapping * get_cell_mapping(int gid)
get cell mapping information for given gid if exist otherwise return NULL.
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int ncell
Definition: multicore.h:64
int end
Definition: multicore.h:65
Memb_list ** _ml_list
Definition: multicore.h:63
Compartment mapping information for NrnThread.
std::vector< std::string > mech_names
Definition: nrnreport.hpp:225
std::vector< int > mech_ids
Definition: nrnreport.hpp:227