25 #include "solver/solver.hpp"
36 using visitor::RenameVisitor;
37 using visitor::VarUsageVisitor;
52 return "C++ (api-compatibility)";
57 return "_check_table_thread";
84 if (optimize_ionvar_copies) {
85 throw std::runtime_error(
"Not implemented.");
97 if (info.point_process) {
98 printer->add_multi_line(R
"CODE(
99 /* Point Process specific functions */
100 static void* _hoc_create_pnt(Object* _ho) {
101 return create_point_process(_pointtype, _ho);
104 printer->push_block("static void _hoc_destroy_pnt(void* _vptr)");
105 if (info.is_watch_used() || info.for_netcon_used) {
106 printer->add_line(
"Prop* _prop = ((Point_process*)_vptr)->prop;");
107 printer->push_block(
"if (_prop)");
108 printer->add_line(
"Datum* _ppvar = _nrn_mechanism_access_dparam(_prop);");
109 if (info.is_watch_used()) {
110 printer->fmt_line(
"_nrn_free_watch(_ppvar, {}, {});",
112 info.is_watch_used());
114 if (info.for_netcon_used) {
115 auto fornetcon_data = get_variable_name(
"fornetcon_data",
false);
116 printer->fmt_line(
"_nrn_free_fornetcon(&{});", fornetcon_data);
118 printer->pop_block();
120 printer->add_line(
"destroy_point_process(_vptr);");
121 printer->pop_block();
122 printer->add_multi_line(R
"CODE(
123 static double _hoc_loc_pnt(void* _vptr) {
124 return loc_point_process(_pointtype, _vptr);
127 static double _hoc_has_loc(void* _vptr) {
128 return has_loc_point(_vptr);
131 static double _hoc_get_loc_pnt(void* _vptr) {
132 return (get_loc_point_process(_vptr));
140 if (info.table_count == 0) {
145 for (
const auto&
function: info.functions_with_table) {
146 auto name =
function->get_node_name();
147 auto internal_params = internal_method_parameters();
148 printer->fmt_line(
"void {}({});",
149 table_update_function_name(name),
150 get_parameter_str(internal_params));
154 {
"",
"size_t",
"",
"id"},
155 {
"",
"Datum*",
"",
"_ppvar"},
156 {
"",
"Datum*",
"",
"_thread"},
157 {
"",
"double*",
"",
"_globals"},
158 {
"",
"NrnThread*",
"",
"nt"},
159 {
"",
"int",
"",
"_type"},
160 {
"",
"const _nrn_model_sorted_token&",
"",
"_sorted_token"}};
164 printer->fmt_line(
"static void {}({})", table_thread_function_name(), get_parameter_str(args));
165 printer->push_block();
166 printer->add_line(
"_nrn_mechanism_cache_range _lmc{_sorted_token, *nt, *_ml, _type};");
167 printer->fmt_line(
"auto inst = make_instance_{}(&_lmc);", info.mod_suffix);
168 if (!info.artificial_cell) {
169 printer->fmt_line(
"auto node_data = make_node_data_{}(*nt, *_ml);", info.mod_suffix);
171 if (!codegen_thread_variables.empty()) {
172 printer->fmt_line(
"auto _thread_vars = {}(_thread[{}].get<double*>());",
173 thread_variables_struct(),
174 info.thread_var_thread_id);
177 for (
const auto&
function: info.functions_with_table) {
178 auto method_name =
function->get_node_name();
179 auto method_args = get_arg_str(internal_method_parameters());
180 printer->fmt_line(
"{}({});", table_update_function_name(method_name), method_args);
182 printer->pop_block();
187 printer->add_line(
"/* Neuron setdata functions */");
188 printer->add_line(
"extern void _nrn_setdata_reg(int, void(*)(Prop*));");
189 printer->push_block(
"static void _setdata(Prop* _prop)");
190 if (!info.point_process) {
191 printer->add_multi_line(R
"CODE(
192 _extcall_prop = _prop;
193 _prop_id = _nrn_get_prop_id(_prop);
196 printer->pop_block();
198 if (info.point_process) {
199 printer->push_block(
"static void _hoc_setdata(void* _vptr)");
200 printer->add_multi_line(R
"CODE(
202 _prop = ((Point_process*)_vptr)->prop;
206 printer->push_block(
"static void _hoc_setdata()");
207 printer->add_multi_line(R
"CODE(
208 Prop *_prop = hoc_getdata_range(mech_type);
213 printer->pop_block();
218 printer->add_newline(2);
220 auto print_decl = [
this](
const auto& callables) {
221 for (
const auto& node: callables) {
222 print_function_declaration(*node, node->get_node_name());
223 printer->add_text(
';');
224 printer->add_newline();
228 printer->add_line(
"/* Mechanism procedures and functions */");
229 print_decl(info.functions);
230 print_decl(info.procedures);
232 for (
const auto& node: info.function_tables) {
233 auto [params, table_params] = function_table_parameters(*node);
234 printer->fmt_line(
"double {}({});",
235 method_name(node->get_node_name()),
236 get_parameter_str(params));
237 printer->fmt_line(
"double {}({});",
238 method_name(
"table_" + node->get_node_name()),
239 get_parameter_str(table_params));
246 const std::string& name,
247 const std::unordered_set<CppObjectSpecifier>& specifiers) {
248 printer->add_newline(2);
249 print_function_declaration(node, name, specifiers);
250 printer->add_text(
" ");
251 printer->push_block();
255 auto type = default_float_data_type();
256 printer->fmt_line(
"{} ret_{} = 0.0;", type, name);
258 printer->fmt_line(
"int ret_{} = 0;", name);
261 if (info.mod_suffix !=
"nothing" && !info.artificial_cell) {
263 "double v = node_data.node_voltages ? "
264 "node_data.node_voltages[node_data.nodeindices[id]] : 0.0;");
268 printer->fmt_line(
"return ret_{};", name);
269 printer->pop_block();
275 if (info.function_uses_table(name)) {
276 auto new_name =
"f_" + name;
277 print_function_or_procedure(node,
280 print_table_check_function(node);
281 print_table_replacement_function(node);
283 print_function_or_procedure(node, name);
288 const ast::Block* function_or_procedure_block,
290 const auto block_name = function_or_procedure_block->
get_node_name();
292 const auto get_func_call_str = [&]() {
293 const auto& params = function_or_procedure_block->
get_parameters();
294 const auto func_proc_name = block_name +
"_" + info.mod_suffix;
295 std::vector<std::string> args;
296 args.reserve(params.size());
297 for (
int i = 0; i < params.size(); ++i) {
298 args.push_back(fmt::format(
"*getarg({})", i + 1));
301 auto internal_args = internal_method_arguments();
302 return fmt::format(
"{}({})",
305 fmt::format(
"{}", fmt::join(args,
", "))));
308 printer->add_line(
"double _r = 0.0;");
310 printer->add_indent();
311 printer->fmt_text(
"_r = {};", get_func_call_str());
312 printer->add_newline();
314 printer->add_line(
"_r = 1.;");
315 printer->fmt_line(
"{};", get_func_call_str());
318 printer->add_line(
"return(_r);");
320 printer->add_line(
"hoc_retpushx(_r);");
325 const ast::Block* function_or_procedure_block,
327 if (info.mod_suffix ==
"nothing") {
331 const auto block_name = function_or_procedure_block->
get_node_name();
332 printer->add_multi_line(R
"CODE(
338 std::string prop_name;
339 if (info.point_process) {
340 printer->add_multi_line(R
"CODE(
341 auto* const _pnt = static_cast<Point_process*>(_vptr);
342 auto* const _p = _pnt->prop;
344 hoc_execerror("POINT_PROCESS data instance not valid", nullptr);
346 _nrn_mechanism_cache_instance _lmc{_p};
348 _ppvar = _nrn_mechanism_access_dparam(_p);
349 _thread = _extcall_thread.data();
350 nt = static_cast<NrnThread*>(_pnt->_vnt);
355 if (program_symtab->lookup(block_name)->has_all_properties(NmodlType::use_range_ptr_var)) {
356 printer->push_block(
"if (!_prop_id)");
358 "hoc_execerror(\"No data for {}_{}. Requires prior call to setdata_{} and that the "
359 "specified mechanism instance still be in existence.\", nullptr);",
363 printer->pop_block();
364 printer->add_line(
"Prop* _local_prop = _extcall_prop;");
366 printer->add_line(
"Prop* _local_prop = _prop_id ? _extcall_prop : nullptr;");
368 printer->add_multi_line(R
"CODE(
369 _nrn_mechanism_cache_instance _lmc{_local_prop};
371 _ppvar = _local_prop ? _nrn_mechanism_access_dparam(_local_prop) : nullptr;
372 _thread = _extcall_thread.data();
375 prop_name = "_local_prop";
377 printer->add_multi_line(R
"CODE(
378 _nrn_mechanism_cache_instance _lmc{_prop};
380 _ppvar = _nrn_mechanism_access_dparam(_prop);
381 _thread = _extcall_thread.data();
387 printer->fmt_line(
"auto inst = make_instance_{}({} ? &_lmc : nullptr);",
390 if (!info.artificial_cell) {
391 printer->fmt_line(
"auto node_data = make_node_data_{}({});", info.mod_suffix, prop_name);
393 if (!codegen_thread_variables.empty()) {
394 printer->fmt_line(
"auto _thread_vars = {}(_thread[{}].get<double*>());",
395 thread_variables_struct(),
396 info.thread_var_thread_id);
398 if (info.function_uses_table(block_name)) {
399 printer->fmt_line(
"{}({});",
400 table_update_function_name(block_name),
401 internal_method_arguments());
407 const ast::Block* function_or_procedure_block,
409 const auto block_name = function_or_procedure_block->
get_node_name();
411 return hoc_function_signature(block_name);
413 return py_function_signature(block_name);
423 printer->push_block(hoc_py_wrapper_signature(function_or_procedure_block, wrapper_type));
425 print_hoc_py_wrapper_setup(function_or_procedure_block, wrapper_type);
426 print_hoc_py_wrapper_call_impl(function_or_procedure_block, wrapper_type);
428 printer->pop_block();
433 auto print_wrappers = [
this](
const auto& callables) {
434 for (
const auto& callable: callables) {
440 print_wrappers(info.procedures);
441 print_wrappers(info.functions);
443 for (
const auto& node: info.function_tables) {
444 auto name = node->get_node_name();
445 auto table_name =
"table_" + node->get_node_name();
448 auto args = std::vector<std::string>{};
449 for (
size_t i = 0; i < node->get_parameters().size(); ++i) {
450 args.push_back(fmt::format(
"*getarg({})", i + 1));
454 std::string return_statement = info.point_process ?
"return _ret;" :
"hoc_retpushx(_ret);";
456 printer->fmt_push_block(
"{}", hoc_function_signature(name));
457 printer->fmt_line(
"double _ret = {}({});", method_name(name), fmt::join(args,
", "));
458 printer->add_line(return_statement);
459 printer->pop_block();
461 printer->fmt_push_block(
"{}", hoc_function_signature(table_name));
462 printer->fmt_line(
"double _ret = {}();", method_name(table_name));
463 printer->add_line(return_statement);
464 printer->pop_block();
467 printer->fmt_push_block(
"{}", py_function_signature(name));
468 printer->fmt_line(
"return {}({});", method_name(name), fmt::join(args,
", "));
469 printer->pop_block();
471 printer->fmt_push_block(
"{}", py_function_signature(table_name));
472 printer->fmt_line(
"return {}();", method_name(table_name));
473 printer->pop_block();
478 return ParamVector{{
"",
"ldifusfunc2_t",
"",
"_f"},
479 {
"",
"const _nrn_model_sorted_token&",
"",
"_sorted_token"},
480 {
"",
"NrnThread&",
"",
"_nt"}};
486 {
"",
"Memb_list*",
"",
"_ml_arg"},
487 {
"",
"size_t",
"",
"id"},
488 {
"",
"Datum*",
"",
"_ppvar"},
489 {
"",
"double*",
"",
"_pdvol"},
490 {
"",
"double*",
"",
"_pdfcdc"},
491 {
"",
"Datum*",
"",
"/* _thread */"},
492 {
"",
"NrnThread*",
"",
"nt"},
493 {
"",
"const _nrn_model_sorted_token&",
"",
"_sorted_token"}};
497 auto coeff_callback_name = [](
const std::string& var_name) {
498 return fmt::format(
"_diffusion_coefficient_{}", var_name);
501 auto space_name = [](
const std::string& var_name) {
502 return fmt::format(
"_diffusion_space_{}", var_name);
505 for (
auto [var_name, values]: info.longitudinal_diffusion_info) {
506 printer->fmt_line(
"static void* {};", space_name(var_name));
507 printer->fmt_push_block(
"static double {}({})",
508 coeff_callback_name(var_name),
509 get_parameter_str(ldifusfunc3_parameters()));
511 print_entrypoint_setup_code_from_memb_list();
513 auto volume_expr = values.volume(
"_i");
514 auto mu_expr = values.diffusion_rate(
"_i");
516 printer->add_indent();
517 printer->add_text(
"*_pdvol= ");
518 volume_expr->accept(*
this);
519 printer->add_text(
";");
520 printer->add_newline();
522 printer->add_line(
"*_pdfcdc = 0.0;");
523 printer->add_indent();
524 printer->add_text(
"return ");
525 mu_expr->accept(*
this);
526 printer->add_text(
";");
527 printer->add_newline();
529 printer->pop_block();
532 printer->fmt_push_block(
"static void _apply_diffusion_function({})",
533 get_parameter_str(ldifusfunc1_parameters()));
534 for (
auto [var_name, values]: info.longitudinal_diffusion_info) {
535 auto var = program_symtab->lookup(var_name);
536 size_t array_size = var->get_length();
537 printer->fmt_push_block(
"for(size_t _i = 0; _i < {}; ++_i)", array_size);
539 "(*_f)(mech_type, {}, &{}, _i, /* x pos */ {}, /* Dx pos */ {}, _sorted_token, _nt);",
540 coeff_callback_name(var_name),
541 space_name(var_name),
542 position_of_float_var(var_name),
543 position_of_float_var(
"D" + var_name));
544 printer->pop_block();
546 printer->pop_block();
547 printer->add_newline();
555 if (info.net_send_used) {
557 variables.back().is_constant =
true;
558 info.tqitem_index =
static_cast<int>(variables.size() - 1);
563 std::vector<IndexVariableInfo>& variables) {
565 variables.back().is_constant =
true;
569 const auto& args = internal_method_parameters();
570 return get_arg_str(args);
575 if (info.mod_suffix ==
"nothing") {
580 params.emplace_back(
"",
"_nrn_mechanism_cache_range&",
"",
"_lmc");
581 params.emplace_back(
"", fmt::format(
"{}&", instance_struct()),
"",
"inst");
582 if (!info.artificial_cell) {
583 params.emplace_back(
"", fmt::format(
"{}&", node_data_struct()),
"",
"node_data");
585 params.emplace_back(
"",
"size_t",
"",
"id");
586 params.emplace_back(
"",
"Datum*",
"",
"_ppvar");
587 params.emplace_back(
"",
"Datum*",
"",
"_thread");
588 if (!codegen_thread_variables.empty()) {
589 params.emplace_back(
"", fmt::format(
"{}&", thread_variables_struct()),
"",
"_thread_vars");
591 params.emplace_back(
"",
"NrnThread*",
"",
"nt");
604 bool table) noexcept {
610 return internal_method_parameters();
615 return {{
"",
"Memb_list*",
"",
"_ml"},
616 {
"",
"size_t",
"",
"_iml"},
617 {
"",
"Datum*",
"",
"_ppvar"},
618 {
"",
"Datum*",
"",
"_thread"},
619 {
"",
"double*",
"",
"_globals"},
620 {
"",
"NrnThread*",
"",
"_nt"}};
635 std::pair<CodegenNeuronCppVisitor::ParamVector, CodegenNeuronCppVisitor::ParamVector>
640 params.emplace_back(
"",
"double",
"", i->get_node_name());
663 auto local_variables = symtab.
get_variables(NmodlType::local_var);
665 if (parent_symtab ==
nullptr) {
666 throw std::runtime_error(
667 "Internal NMODL error: non top-level symbol table doesn't have a parent.");
672 for (
const auto& symbol: local_variables) {
673 auto status = symbol->get_status();
676 auto current_name = symbol->get_name();
677 auto mod_name = is_renamed ? symbol->get_original_name() : current_name;
679 variable_names[mod_name] = current_name;
682 return variable_names;
688 const std::string& verbatim) {
695 std::vector<std::string> macros_defined;
696 auto print_macro = [
this, &verbatim, ¯os_defined](
const std::string& macro_name,
697 const std::string& macro_value) {
698 if (verbatim.find(macro_name) != std::string::npos) {
699 printer->fmt_line(
"#define {} {}", macro_name, macro_value);
700 macros_defined.push_back(macro_name);
704 printer->add_line(
"// Setup for VERBATIM");
705 for (
const auto& var: codegen_float_variables) {
707 print_macro(name, get_variable_name(name));
710 for (
const auto& var: codegen_int_variables) {
712 std::string macro_value = get_variable_name(name);
713 print_macro(name, macro_value);
714 if (verbatim.find(
"_p_" + name) != std::string::npos) {
715 print_macro(
"_p_" + name, get_pointer_name(name));
719 for (
const auto& var: codegen_global_variables) {
721 print_macro(name, get_variable_name(name));
724 for (
const auto& var: codegen_thread_variables) {
726 print_macro(name, get_variable_name(name));
729 for (
const auto& func: info.functions) {
731 print_macro(name, method_name(name));
732 print_macro(fmt::format(
"_l{}", name), fmt::format(
"ret_{}", name));
735 for (
const auto& proc: info.procedures) {
737 print_macro(name, method_name(name));
743 for (
const auto& [mod_name, current_name]: locals) {
744 print_macro(fmt::format(
"_l{}", mod_name), get_variable_name(current_name));
748 print_macro(
"_nt",
"nt");
749 print_macro(
"_tqitem",
"tqitem");
751 auto print_args_macro = [
this, print_macro](
const std::string& macro_basename,
753 print_macro(
"_" + macro_basename +
"_", get_arg_str(params));
754 print_macro(
"_" + macro_basename +
"comma_", get_arg_str(params) +
",");
755 print_macro(
"_" + macro_basename +
"proto_", get_parameter_str(params));
756 print_macro(
"_" + macro_basename +
"protocomma_", get_parameter_str(params) +
",");
759 print_args_macro(
"internalthreadargs", internalthreadargs_parameters());
760 print_args_macro(
"threadargs", threadargs_parameters());
762 return macros_defined;
767 const std::vector<std::string>& macros_defined) {
768 for (
const auto& macro: macros_defined) {
769 printer->fmt_line(
"#undef {}", macro);
771 printer->add_line(
"// End of cleanup for VERBATIM");
777 driver.scan_string(verbatim);
778 auto tokens =
driver.all_tokens();
780 for (
size_t i = 0; i < tokens.size(); i++) {
781 auto token = tokens[i];
785 if (program_symtab->is_method_defined(token) && tokens[i + 1] ==
"(") {
786 result += token +
"(";
792 result += tokens[i + 2];
806 auto massaged_verbatim = process_verbatim_text(verbatim_code);
808 auto macros_defined = print_verbatim_setup(node, massaged_verbatim);
809 printer->add_line(
"// Begin VERBATIM");
811 for (
const auto& line: lines) {
812 printer->add_line(line);
814 printer->add_line(
"// End VERBATIM");
815 print_verbatim_cleanup(macros_defined);
826 const std::string& function_or_procedure_name)
const {
827 return fmt::format(
"_hoc_{}", function_or_procedure_name);
832 const std::string& function_or_procedure_name)
const {
833 return fmt::format(
"static {} {}({})",
834 info.point_process ?
"double" :
"void",
835 hoc_function_name(function_or_procedure_name),
836 info.point_process ?
"void * _vptr" :
"");
841 const std::string& function_or_procedure_name)
const {
842 return fmt::format(
"_npy_{}", function_or_procedure_name);
847 const std::string& function_or_procedure_name)
const {
848 return fmt::format(
"static double {}(Prop* _prop)",
849 py_function_name(function_or_procedure_name));
863 std::vector<ShadowUseStatement>& statements,
865 const std::string& ) {
866 auto ion_name = ion.
name;
867 int dparam_index = get_int_variable_index(fmt::format(
"style_{}", ion_name));
869 auto style_name = fmt::format(
"_style_{}", ion_name);
870 auto style_stmt = fmt::format(
"int {} = *(_ppvar[{}].get<int*>())", style_name, dparam_index);
874 auto wrote_conc_stmt = fmt::format(
"nrn_wrote_conc(_{}_sym, {}, {}, {}, {})",
889 bool use_instance)
const {
891 throw std::runtime_error(
"Printing non-instance variables is not implemented.");
894 auto name = symbol->get_name();
895 auto dimension = symbol->get_length();
896 if (symbol->is_array()) {
897 return fmt::format(
"(inst.{}+id*{})", name, dimension);
899 return fmt::format(
"inst.{}[id]", name);
905 const std::string& name,
906 bool use_instance)
const {
907 auto position = position_of_int_var(name);
910 return fmt::format(
"(nrnran123_State*) _ppvar[{}].literal_value<void*>()", position);
914 return fmt::format(
"_ppvar[{}].literal_value<void*>()", position);
918 return fmt::format(
"(*_ppvar[{}].get<double*>())", position);
923 throw std::runtime_error(
"Not implemented. [wiejo]");
926 throw std::runtime_error(
"Not implemented. [ncuwi]");
931 return fmt::format(
"inst.{}[id]", name);
933 return fmt::format(
"_ppvar[{}]", position);
936 return fmt::format(
"(*inst.{}[id])", name);
940 throw std::runtime_error(
"Not implemented. [nvueir]");
945 bool use_instance)
const {
946 auto i_var = var_info.
offset;
947 auto var_name = var_info.
symbol->get_name();
950 if (var_info.
symbol->is_array()) {
951 return fmt::format(
"(_thread_vars.{}_ptr(id))", var_name);
953 return fmt::format(
"_thread_vars.{}(id)", var_name);
956 if (var_info.
symbol->is_array()) {
957 return fmt::format(
"({}.thread_data + {})", global_struct_instance(), i_var);
959 return fmt::format(
"{}.thread_data[{}]", global_struct_instance(), i_var);
966 bool use_instance)
const {
970 return fmt::format(
"{}.{}", global_struct_instance(), symbol->get_name());
976 auto name_comparator = [&name](
const auto& sym) {
return name ==
get_name(sym); };
979 std::find_if(codegen_int_variables.begin(), codegen_int_variables.end(), name_comparator);
981 if (var == codegen_int_variables.end()) {
982 throw std::runtime_error(
"Only integer variables have a 'pointer name'.");
984 auto position = position_of_int_var(name);
985 return fmt::format(
"_ppvar[{}].literal_value<void*>()", position);
990 bool use_instance)
const {
991 std::string varname = update_if_ion_variable_name(name);
992 if (!info.artificial_cell && varname ==
"v") {
996 auto name_comparator = [&varname](
const auto& sym) {
return varname ==
get_name(sym); };
999 if (printing_net_receive) {
1009 return fmt::format(
"{}.{}", global_struct_instance(), name);
1013 auto f = std::find_if(codegen_float_variables.begin(),
1014 codegen_float_variables.end(),
1016 if (f != codegen_float_variables.end()) {
1017 return float_variable_name(*f, use_instance);
1022 std::find_if(codegen_int_variables.begin(), codegen_int_variables.end(), name_comparator);
1023 if (i != codegen_int_variables.end()) {
1024 return int_variable_name(*i, varname, use_instance);
1028 auto t = std::find_if(codegen_thread_variables.begin(),
1029 codegen_thread_variables.end(),
1031 if (t != codegen_thread_variables.end()) {
1032 return thread_variable_name(*t, use_instance);
1036 auto g = std::find_if(codegen_global_variables.begin(),
1037 codegen_global_variables.end(),
1039 if (g != codegen_global_variables.end()) {
1040 return global_variable_name(*g, use_instance);
1052 auto e = std::find_if(info.external_variables.begin(),
1053 info.external_variables.end(),
1055 if (e != info.external_variables.end()) {
1056 return fmt::format(
"{}()", varname);
1060 std::find_if(info.neuron_global_variables.begin(),
1061 info.neuron_global_variables.end(),
1062 [&varname](
auto const& entry) { return entry.first->get_name() == varname; });
1063 if (iter != info.neuron_global_variables.end()) {
1068 ret.append(varname);
1086 printer->add_newline();
1087 printer->add_multi_line(R
"CODE(
1088 #include <Eigen/Dense>
1095 if (info.eigen_newton_solver_exist) {
1096 printer->add_multi_line(nmodl::solvers::newton_hpp);
1102 printer->add_newline();
1103 printer->add_multi_line(R
"CODE(
1104 #include "mech_api.h"
1105 #include "neuron/cache/mechanism_range.hpp"
1106 #include "nmodlmutex.h"
1107 #include "nrniv_mf.h"
1108 #include "section_fwd.hpp"
1116 printer->add_newline(2);
1117 printer->push_block(
"static void _initlists()");
1118 for (
auto i = 0; i < info.prime_variables_by_order.size(); ++i) {
1119 const auto& prime_var = info.prime_variables_by_order[i];
1124 if (prime_var->is_array()) {
1128 printer->fmt_push_block(
"for (int _i = 0; _i < {}; ++_i)", prime_var->get_length());
1129 printer->fmt_line(
"/* {}[{}] */", prime_var->get_name(), prime_var->get_length());
1130 printer->fmt_line(
"_slist1[{}+_i] = {{{}, _i}};",
1132 position_of_float_var(prime_var->get_name()));
1133 const auto prime_var_deriv_name =
"D" + prime_var->get_name();
1134 printer->fmt_line(
"/* {}[{}] */", prime_var_deriv_name, prime_var->get_length());
1135 printer->fmt_line(
"_dlist1[{}+_i] = {{{}, _i}};",
1137 position_of_float_var(prime_var_deriv_name));
1138 printer->pop_block();
1140 printer->fmt_line(
"/* {} */", prime_var->get_name());
1141 printer->fmt_line(
"_slist1[{}] = {{{}, 0}};",
1143 position_of_float_var(prime_var->get_name()));
1144 const auto prime_var_deriv_name =
"D" + prime_var->get_name();
1145 printer->fmt_line(
"/* {} */", prime_var_deriv_name);
1146 printer->fmt_line(
"_dlist1[{}] = {{{}, 0}};",
1148 position_of_float_var(prime_var_deriv_name));
1151 printer->pop_block();
1155 auto params = internal_method_parameters();
1161 const auto value_initialize = print_initializers ?
"{}" :
"";
1164 printer->add_newline(2);
1165 printer->add_line(
"/* NEURON global variables */");
1166 if (info.primes_size != 0) {
1167 printer->fmt_line(
"static neuron::container::field_index _slist1[{0}], _dlist1[{0}];",
1171 for (
const auto& ion: info.ions) {
1172 printer->fmt_line(
"static Symbol* _{}_sym;", ion.name);
1175 if (info.emit_cvode) {
1176 printer->add_line(
"static Symbol** _atollist;");
1177 printer->push_block(
"static HocStateTolerance _hoc_state_tol[] =");
1178 printer->add_line(
"{0, 0}");
1179 printer->pop_block(
";");
1182 printer->add_line(
"static int mech_type;");
1184 if (info.point_process) {
1185 printer->add_line(
"static int _pointtype;");
1187 printer->add_multi_line(R
"CODE(
1188 static Prop* _extcall_prop;
1189 /* _prop_id kind of shadows _extcall_prop to allow validity checking. */
1190 static _nrn_non_owning_id_without_container _prop_id{};)CODE");
1193 printer->add_line("static _nrn_mechanism_std_vector<Datum> _extcall_thread;");
1196 auto float_type = default_float_data_type();
1197 printer->add_newline(2);
1198 printer->add_line(
"/** all global variables */");
1199 printer->fmt_push_block(
"struct {}", global_struct());
1201 if (!info.ions.empty()) {
1205 if (!info.thread_variables.empty()) {
1206 size_t prefix_sum = 0;
1207 for (
size_t i = 0; i < info.thread_variables.size(); ++i) {
1208 const auto& var = info.thread_variables[i];
1209 codegen_thread_variables.push_back({var, i, prefix_sum});
1211 prefix_sum += var->get_length();
1216 for (
const auto& var: info.global_variables) {
1217 codegen_global_variables.push_back(var);
1220 if (info.vectorize && !info.top_local_variables.empty()) {
1221 size_t prefix_sum = info.thread_var_data_size;
1222 size_t n_thread_vars = codegen_thread_variables.size();
1223 for (
size_t i = 0; i < info.top_local_variables.size(); ++i) {
1224 const auto& var = info.top_local_variables[i];
1225 codegen_thread_variables.push_back({var, n_thread_vars + i, prefix_sum});
1227 prefix_sum += var->get_length();
1231 if (!info.vectorize && !info.top_local_variables.empty()) {
1232 for (
size_t i = 0; i < info.top_local_variables.size(); ++i) {
1233 const auto& var = info.top_local_variables[i];
1234 codegen_global_variables.push_back(var);
1239 if (!codegen_thread_variables.empty()) {
1240 if (!info.vectorize) {
1242 throw std::runtime_error(
"Found thread variables with `vectorize == false`.");
1245 codegen_global_variables.push_back(make_symbol(
"thread_data_in_use"));
1247 auto symbol = make_symbol(
"thread_data");
1248 auto thread_data_size = info.thread_var_data_size + info.top_local_thread_size;
1249 symbol->set_as_array(thread_data_size);
1250 codegen_global_variables.push_back(symbol);
1253 for (
const auto& var: info.state_vars) {
1254 auto name = var->get_name() +
"0";
1255 auto symbol = program_symtab->lookup(name);
1256 if (symbol ==
nullptr) {
1257 codegen_global_variables.push_back(make_symbol(name));
1261 for (
const auto& var: info.constant_variables) {
1262 codegen_global_variables.push_back(var);
1265 for (
const auto& var: codegen_global_variables) {
1266 auto name = var->get_name();
1267 auto length = var->get_length();
1268 if (var->is_array()) {
1269 printer->fmt_line(
"{} {}[{}] /* TODO init const-array */;", float_type, name, length);
1272 if (
auto const& value_ptr = var->get_value()) {
1275 printer->fmt_line(
"{} {}{};",
1278 print_initializers ? fmt::format(
"{{{:g}}}", value) : std::string{});
1282 if (info.table_count > 0) {
1284 printer->fmt_line(
"double usetable{};", print_initializers ?
"{1}" :
"");
1287 for (
const auto& block: info.functions_with_table) {
1288 const auto& name = block->get_node_name();
1289 printer->fmt_line(
"{} tmin_{}{};", float_type, name, value_initialize);
1290 printer->fmt_line(
"{} mfac_{}{};", float_type, name, value_initialize);
1291 codegen_global_variables.push_back(make_symbol(
"tmin_" + name));
1292 codegen_global_variables.push_back(make_symbol(
"mfac_" + name));
1295 for (
const auto& variable: info.table_statement_variables) {
1296 auto const name =
"t_" + variable->get_name();
1297 auto const num_values = variable->get_num_values();
1298 if (variable->is_array()) {
1299 int array_len = variable->get_length();
1301 "{} {}[{}][{}]{};", float_type, name, array_len, num_values, value_initialize);
1303 printer->fmt_line(
"{} {}[{}]{};", float_type, name, num_values, value_initialize);
1305 codegen_global_variables.push_back(make_symbol(name));
1309 print_global_struct_function_table_ptrs();
1311 if (info.vectorize && info.thread_data_index) {
1316 if (info.diam_used) {
1317 printer->fmt_line(
"Symbol* _morphology_sym;");
1320 printer->pop_block(
";");
1322 print_global_var_struct_assertions();
1323 print_global_var_struct_decl();
1324 print_global_var_external_access();
1326 print_global_param_default_values();
1330 for (
const auto& var: codegen_global_variables) {
1332 auto var_expr = get_variable_name(var_name,
false);
1334 printer->fmt_push_block(
"auto {}() -> std::decay<decltype({})>::type ",
1335 method_name(var_name),
1337 printer->fmt_line(
"return {};", var_expr);
1338 printer->pop_block();
1340 if (!codegen_global_variables.empty()) {
1341 printer->add_newline();
1344 for (
const auto& var: info.external_variables) {
1346 printer->fmt_line(
"double {}();", var_name);
1348 if (!info.external_variables.empty()) {
1349 printer->add_newline();
1354 printer->push_block(
"static std::vector<double> _parameter_defaults =");
1356 std::vector<std::string> defaults;
1357 for (
const auto& p: info.range_parameter_vars) {
1358 double value = p->get_value() ==
nullptr ? 0.0 : *p->get_value();
1359 defaults.push_back(fmt::format(
"{:g} /* {} */", value, p->get_name()));
1362 printer->add_multi_line(fmt::format(
"{}", fmt::join(defaults,
",\n")));
1363 printer->pop_block(
";");
1367 auto variable_printer = [&](
const std::vector<SymbolType>& variables,
bool if_array) {
1368 for (
const auto& variable: variables) {
1369 if (variable->is_array() == if_array) {
1372 auto name = get_variable_name(variable->get_name(),
false);
1373 auto ename = add_escape_quote(variable->get_name() +
"_" + info.mod_suffix);
1375 auto length = variable->get_length();
1376 printer->fmt_line(
"{{{}, {}, {}}},", ename, name, length);
1378 printer->fmt_line(
"{{{}, &{}}},", ename, name);
1384 auto globals = info.global_variables;
1385 auto thread_vars = info.thread_variables;
1387 if (info.table_count > 0) {
1391 printer->add_newline(2);
1392 printer->add_line(
"/** connect global (scalar) variables to hoc -- */");
1393 printer->add_line(
"static DoubScal hoc_scalar_double[] = {");
1394 printer->increase_indent();
1395 variable_printer(globals,
false);
1396 variable_printer(thread_vars,
false);
1397 printer->add_line(
"{nullptr, nullptr}");
1398 printer->decrease_indent();
1399 printer->add_line(
"};");
1401 printer->add_newline(2);
1402 printer->add_line(
"/** connect global (array) variables to hoc -- */");
1403 printer->add_line(
"static DoubVec hoc_vector_double[] = {");
1404 printer->increase_indent();
1405 variable_printer(globals,
true);
1406 variable_printer(thread_vars,
true);
1407 printer->add_line(
"{nullptr, nullptr, 0}");
1408 printer->decrease_indent();
1409 printer->add_line(
"};");
1411 printer->add_newline(2);
1412 printer->add_line(
"/* declaration of user functions */");
1414 auto print_entrypoint_decl = [
this](
const auto& callables,
auto get_name) {
1415 for (
const auto& node: callables) {
1417 printer->fmt_line(
"{};", hoc_function_signature(name));
1419 if (!info.point_process) {
1420 printer->fmt_line(
"{};", py_function_signature(name));
1425 auto get_name = [](
const auto& node) {
return node->get_node_name(); };
1426 print_entrypoint_decl(info.functions,
get_name);
1427 print_entrypoint_decl(info.procedures,
get_name);
1428 print_entrypoint_decl(info.function_tables,
get_name);
1429 print_entrypoint_decl(info.function_tables, [](
const auto& node) {
1430 auto node_name = node->get_node_name();
1431 return
"table_" + node_name;
1434 printer->add_newline(2);
1435 printer->add_line(
"/* connect user functions to hoc names */");
1436 printer->add_line(
"static VoidFunc hoc_intfunc[] = {");
1437 printer->increase_indent();
1438 if (info.point_process) {
1439 printer->add_line(
"{0, 0}");
1440 printer->decrease_indent();
1441 printer->add_line(
"};");
1442 printer->add_line(
"static Member_func _member_func[] = {");
1443 printer->increase_indent();
1444 printer->add_multi_line(R
"CODE(
1445 {"loc", _hoc_loc_pnt},
1446 {"has_loc", _hoc_has_loc},
1447 {"get_loc", _hoc_get_loc_pnt},)CODE");
1449 if (info.mod_suffix !=
"nothing") {
1450 printer->fmt_line(
"{{\"setdata_{}\", _hoc_setdata}},", info.mod_suffix);
1454 auto print_callable_reg = [
this](
const auto& callables,
auto get_name) {
1455 for (
const auto& node: callables) {
1457 printer->fmt_line(
"{{\"{}{}\", {}}},", name, info.rsuffix, hoc_function_name(name));
1461 print_callable_reg(info.procedures,
get_name);
1462 print_callable_reg(info.functions,
get_name);
1463 print_callable_reg(info.function_tables,
get_name);
1464 print_callable_reg(info.function_tables, [](
const auto& node) {
1465 auto node_name = node->get_node_name();
1466 return
"table_" + node_name;
1469 printer->add_line(
"{nullptr, nullptr}");
1470 printer->decrease_indent();
1471 printer->add_line(
"};");
1474 auto print_py_callable_reg = [
this](
const auto& callables,
auto get_name) {
1475 for (
const auto& callable: callables) {
1476 const auto name =
get_name(callable);
1477 printer->fmt_line(
"{{\"{}\", {}}},", name, py_function_name(name));
1481 if (!info.point_process) {
1482 printer->push_block(
"static NPyDirectMechFunc npy_direct_func_proc[] =");
1483 print_py_callable_reg(info.procedures,
1484 [](
const auto& callable) { return callable->get_node_name(); });
1485 print_py_callable_reg(info.functions,
1486 [](
const auto& callable) { return callable->get_node_name(); });
1487 print_py_callable_reg(info.function_tables,
1488 [](
const auto& callable) { return callable->get_node_name(); });
1489 print_py_callable_reg(info.function_tables, [](
const auto& callable) {
1490 return
"table_" + callable->get_node_name();
1492 printer->add_line(
"{nullptr, nullptr}");
1493 printer->pop_block(
";");
1498 printer->add_newline(2);
1499 printer->fmt_push_block(
"extern \"C\" void _{}_reg()", info.mod_file);
1500 if (info.mod_suffix ==
"nothing") {
1501 print_mechanism_register_nothing();
1503 print_mechanism_register_regular();
1505 printer->pop_block();
1509 printer->add_line(
"_initlists();");
1510 printer->add_newline();
1512 for (
const auto& ion: info.ions) {
1513 double valence = ion.valence.value_or(-10000.0);
1514 printer->fmt_line(
"ion_reg(\"{}\", {});", ion.name, valence);
1516 if (!info.ions.empty()) {
1517 printer->add_newline();
1520 for (
const auto& ion: info.ions) {
1521 printer->fmt_line(
"_{0}_sym = hoc_lookup(\"{0}_ion\");", ion.name);
1523 if (!info.ions.empty()) {
1524 printer->add_newline();
1527 const auto compute_functions_parameters =
1529 ? fmt::format(
"{}, {}, {}",
1533 :
"nullptr, nullptr, nullptr";
1536 const auto register_mech_args = fmt::format(
"{}, {}, {}, {}, {}, {}",
1537 get_channel_info_var_name(),
1539 compute_functions_parameters,
1541 info.first_pointer_var_index,
1542 1 + info.thread_data_index);
1543 if (info.point_process) {
1545 "_pointtype = point_register_mech({}, _hoc_create_pnt, _hoc_destroy_pnt, "
1547 register_mech_args);
1549 if (info.destructor_node) {
1550 printer->fmt_line(
"register_destructor({});",
1554 printer->fmt_line(
"register_mech({});", register_mech_args);
1558 if (info.thread_callback_register) {
1559 printer->fmt_line(
"_extcall_thread.resize({});", info.thread_data_index + 1);
1560 printer->fmt_line(
"thread_mem_init(_extcall_thread.data());");
1561 printer->fmt_line(
"{} = 0;", get_variable_name(
"thread_data_in_use",
false));
1566 printer->add_newline();
1567 printer->fmt_line(
"mech_type = nrn_get_mechtype({}[1]);", get_channel_info_var_name());
1569 printer->add_line(
"hoc_register_parm_default(mech_type, &_parameter_defaults);");
1572 if (info.table_count > 0) {
1573 printer->fmt_line(
"_nrn_thread_table_reg(mech_type, {});", table_thread_function_name());
1576 printer->add_line(
"_nrn_mechanism_register_data_fields(mech_type,");
1577 printer->increase_indent();
1579 const auto codegen_float_variables_size = codegen_float_variables.size();
1580 std::vector<std::string> mech_register_args;
1582 for (
int i = 0; i < codegen_float_variables_size; ++i) {
1583 const auto& float_var = codegen_float_variables[i];
1584 if (float_var->is_array()) {
1585 mech_register_args.push_back(
1586 fmt::format(
"_nrn_mechanism_field<double>{{\"{}\", {}}} /* {} */",
1587 float_var->get_name(),
1588 float_var->get_length(),
1591 mech_register_args.push_back(fmt::format(
1592 "_nrn_mechanism_field<double>{{\"{}\"}} /* {} */", float_var->get_name(), i));
1596 const auto codegen_int_variables_size = codegen_int_variables.size();
1597 for (
int i = 0; i < codegen_int_variables_size; ++i) {
1598 const auto& int_var = codegen_int_variables[i];
1599 const auto& name = int_var.symbol->get_name();
1600 if (i != info.semantics[i].index) {
1601 throw std::runtime_error(
"Broken logic.");
1603 const auto& semantic = info.semantics[i].name;
1605 auto type =
"double*";
1607 type =
"Point_process*";
1618 mech_register_args.push_back(
1619 fmt::format(
"_nrn_mechanism_field<{}>{{\"{}\", \"{}\"}} /* {} */",
1622 info.semantics[i].name,
1626 if (info.emit_cvode) {
1627 mech_register_args.push_back(
1628 fmt::format(
"_nrn_mechanism_field<int>{{\"{}\", \"cvodeieq\"}} /* {} */",
1630 codegen_int_variables_size));
1633 printer->add_multi_line(fmt::format(
"{}", fmt::join(mech_register_args,
",\n")));
1635 printer->decrease_indent();
1636 printer->add_line(
");");
1637 printer->add_newline();
1639 printer->fmt_line(
"hoc_register_prop_size(mech_type, {}, {});",
1640 float_variables_size(),
1641 int_variables_size() +
static_cast<int>(info.emit_cvode));
1643 for (
int i = 0; i < codegen_int_variables_size; ++i) {
1644 if (i != info.semantics[i].index) {
1645 throw std::runtime_error(
"Broken logic.");
1648 printer->fmt_line(
"hoc_register_dparam_semantics(mech_type, {}, \"{}\");",
1650 info.semantics[i].name);
1653 if (!info.longitudinal_diffusion_info.empty()) {
1654 printer->fmt_line(
"hoc_register_ldifus1(_apply_diffusion_function);");
1658 if (info.write_concentration) {
1659 printer->fmt_line(
"nrn_writes_conc(mech_type, 0);");
1662 if (info.artificial_cell) {
1663 printer->fmt_line(
"add_nrn_artcell(mech_type, {});", info.tqitem_index);
1666 if (info.net_event_used) {
1667 printer->add_line(
"add_nrn_has_net_event(mech_type);");
1670 if (info.for_netcon_used) {
1672 std::find_if(info.semantics.begin(), info.semantics.end(), [](
const IndexSemantics& a) {
1673 return a.name == naming::FOR_NETCON_SEMANTIC;
1675 if (dparam_it == info.semantics.end()) {
1676 throw std::runtime_error(
"Couldn't find `fornetcon` variable.");
1679 int dparam_index = dparam_it->index;
1680 printer->fmt_line(
"add_nrn_fornetcons(mech_type, {});", dparam_index);
1683 printer->add_line(
"hoc_register_var(hoc_scalar_double, hoc_vector_double, hoc_intfunc);");
1684 if (!info.point_process) {
1685 printer->add_line(
"hoc_register_npy_direct(mech_type, npy_direct_func_proc);");
1687 if (info.net_receive_node) {
1688 printer->fmt_line(
"pnt_receive[mech_type] = nrn_net_receive_{};", info.mod_suffix);
1689 printer->fmt_line(
"pnt_receive_size[mech_type] = {};", info.num_net_receive_parameters);
1692 if (info.net_receive_initial_node) {
1693 printer->fmt_line(
"pnt_receive_init[mech_type] = net_init;");
1696 if (info.thread_callback_register) {
1697 printer->add_line(
"_nrn_thread_reg(mech_type, 1, thread_mem_init);");
1698 printer->add_line(
"_nrn_thread_reg(mech_type, 0, thread_mem_cleanup);");
1701 if (info.diam_used) {
1702 printer->fmt_line(
"{}._morphology_sym = hoc_lookup(\"morphology\");",
1703 global_struct_instance());
1706 if (info.emit_cvode) {
1707 printer->fmt_line(
"hoc_register_dparam_semantics(mech_type, {}, \"cvodeieq\");",
1708 codegen_int_variables_size);
1709 printer->fmt_line(
"hoc_register_cvode(mech_type, {}, {}, {}, {});",
1714 printer->fmt_line(
"hoc_register_tolerance(mech_type, _hoc_state_tol, &_atollist);");
1720 printer->add_line(
"hoc_register_var(hoc_scalar_double, hoc_vector_double, hoc_intfunc);");
1725 if (!info.thread_callback_register) {
1729 auto static_thread_data = get_variable_name(
"thread_data",
false);
1730 auto inuse = get_variable_name(
"thread_data_in_use",
false);
1731 auto thread_data_index = info.thread_var_thread_id;
1732 printer->push_block(
"static void thread_mem_init(Datum* _thread) ");
1733 printer->push_block(fmt::format(
"if({})", inuse));
1734 printer->fmt_line(
"_thread[{}] = {{neuron::container::do_not_search, new double[{}]{{}}}};",
1736 info.thread_var_data_size + info.top_local_thread_size);
1737 printer->pop_block();
1738 printer->push_block(
"else");
1739 printer->fmt_line(
"_thread[{}] = {{neuron::container::do_not_search, {}}};",
1741 static_thread_data);
1742 printer->fmt_line(
"{} = 1;", inuse);
1743 printer->pop_block();
1744 printer->pop_block();
1746 printer->push_block(
"static void thread_mem_cleanup(Datum* _thread) ");
1747 printer->fmt_line(
"double * _thread_data_ptr = _thread[{}].get<double*>();", thread_data_index);
1748 printer->push_block(fmt::format(
"if(_thread_data_ptr == {})", static_thread_data));
1749 printer->fmt_line(
"{} = 0;", inuse);
1750 printer->pop_block();
1751 printer->push_block(
"else");
1752 printer->add_line(
"delete[] _thread_data_ptr;");
1753 printer->pop_block();
1754 printer->pop_block();
1759 for (
auto const& [var, type]: info.neuron_global_variables) {
1760 auto const name = var->get_name();
1761 printer->fmt_line(
"extern {} {};", type, name);
1766 auto const value_initialize = print_initializers ?
"{}" :
"";
1767 printer->add_newline(2);
1768 printer->add_line(
"/** all mechanism instance variables and global variables */");
1769 printer->fmt_push_block(
"struct {} ", instance_struct());
1771 for (
auto const& [var, type]: info.neuron_global_variables) {
1772 auto const name = var->get_name();
1773 printer->fmt_line(
"{}* {}{};",
1776 print_initializers ? fmt::format(
"{{&::{}}}", name) : std::string{});
1778 for (
auto& var: codegen_float_variables) {
1779 const auto& name = var->get_name();
1780 printer->fmt_line(
"double* {}{};", name, value_initialize);
1782 for (
auto& var: codegen_int_variables) {
1783 const auto& name = var.symbol->get_name();
1784 auto position = position_of_int_var(name);
1788 }
else if (var.is_index || var.is_integer) {
1793 auto qualifier = var.is_constant ?
"const " :
"";
1794 auto type = var.is_vdata ?
"void*" : default_float_data_type();
1795 printer->fmt_line(
"{}{}* const* {}{};", qualifier, type, name, value_initialize);
1799 printer->fmt_line(
"{}* {}{};",
1802 print_initializers ? fmt::format(
"{{&{}}}", global_struct_instance())
1804 printer->pop_block(
";");
1808 printer->add_newline(2);
1809 printer->fmt_push_block(
"static {} make_instance_{}(_nrn_mechanism_cache_range* _lmc)",
1813 printer->push_block(
"if(_lmc == nullptr)");
1814 printer->fmt_line(
"return {}();", instance_struct());
1815 printer->pop_block_nl(2);
1817 printer->fmt_push_block(
"return {}", instance_struct());
1819 std::vector<std::string> make_instance_args;
1822 for (
auto const& [var, type]: info.neuron_global_variables) {
1823 auto const name = var->get_name();
1824 make_instance_args.push_back(fmt::format(
"&::{}", name));
1828 const auto codegen_float_variables_size = codegen_float_variables.size();
1829 for (
int i = 0; i < codegen_float_variables_size; ++i) {
1830 const auto& float_var = codegen_float_variables[i];
1831 if (float_var->is_array()) {
1832 make_instance_args.push_back(
1833 fmt::format(
"_lmc->template data_array_ptr<{}, {}>()", i, float_var->get_length()));
1835 make_instance_args.push_back(fmt::format(
"_lmc->template fpfield_ptr<{}>()", i));
1839 const auto codegen_int_variables_size = codegen_int_variables.size();
1840 for (
size_t i = 0; i < codegen_int_variables_size; ++i) {
1841 const auto& var = codegen_int_variables[i];
1842 auto name = var.symbol->get_name();
1843 auto sem = info.semantics[i].name;
1844 auto const variable = [&var, &sem, i]() -> std::string {
1845 if (var.is_index || var.is_integer) {
1847 }
else if (var.is_vdata) {
1852 return fmt::format(
"_lmc->template dptr_field_ptr<{}>()", i);
1855 if (variable !=
"") {
1856 make_instance_args.push_back(variable);
1860 printer->add_multi_line(fmt::format(
"{}", fmt::join(make_instance_args,
",\n")));
1862 printer->pop_block(
";");
1863 printer->pop_block();
1867 printer->add_newline(2);
1868 printer->fmt_push_block(
"struct {} ", node_data_struct());
1871 printer->add_line(
"int const * nodeindices;");
1872 printer->add_line(
"double const * node_voltages;");
1873 printer->add_line(
"double * node_diagonal;");
1874 printer->add_line(
"double * node_rhs;");
1875 printer->add_line(
"int nodecount;");
1877 printer->pop_block(
";");
1881 printer->add_newline(2);
1882 printer->fmt_push_block(
"static {} make_node_data_{}(NrnThread& nt, Memb_list& _ml_arg)",
1886 std::vector<std::string> make_node_data_args = {
"_ml_arg.nodeindices",
1887 "nt.node_voltage_storage()",
1888 "nt.node_d_storage()",
1889 "nt.node_rhs_storage()",
1890 "_ml_arg.nodecount"};
1892 printer->fmt_push_block(
"return {}", node_data_struct());
1893 printer->add_multi_line(fmt::format(
"{}", fmt::join(make_node_data_args,
",\n")));
1895 printer->pop_block(
";");
1896 printer->pop_block();
1899 printer->fmt_push_block(
"static {} make_node_data_{}(Prop * _prop)",
1903 printer->push_block(
"if(!_prop)");
1904 printer->fmt_line(
"return {}();", node_data_struct());
1905 printer->pop_block_nl(2);
1907 printer->add_line(
"static std::vector<int> node_index{0};");
1908 printer->add_line(
"Node* _node = _nrn_mechanism_access_node(_prop);");
1910 make_node_data_args = {
"node_index.data()",
1911 "&_nrn_mechanism_access_voltage(_node)",
1912 "&_nrn_mechanism_access_d(_node)",
1913 "&_nrn_mechanism_access_rhs(_node)",
1916 printer->fmt_push_block(
"return {}", node_data_struct());
1917 printer->add_multi_line(fmt::format(
"{}", fmt::join(make_node_data_args,
",\n")));
1919 printer->pop_block(
";");
1920 printer->pop_block();
1921 printer->add_newline();
1925 if (codegen_thread_variables.empty()) {
1929 printer->add_newline(2);
1930 printer->fmt_push_block(
"struct {} ", thread_variables_struct());
1931 printer->add_line(
"double * thread_data;");
1932 printer->add_newline();
1934 std::string simd_width =
"1";
1937 for (
const auto& var_info: codegen_thread_variables) {
1938 printer->fmt_push_block(
"double * {}_ptr(size_t id)", var_info.symbol->get_name());
1939 printer->fmt_line(
"return thread_data + {} + (id % {});", var_info.offset, simd_width);
1940 printer->pop_block();
1942 printer->fmt_push_block(
"double & {}(size_t id)", var_info.symbol->get_name());
1943 printer->fmt_line(
"return thread_data[{} + (id % {})];", var_info.offset, simd_width);
1944 printer->pop_block();
1946 printer->add_newline();
1948 printer->push_block(fmt::format(
"{}(double * const thread_data)", thread_variables_struct()));
1949 printer->fmt_line(
"this->thread_data = thread_data;");
1950 printer->pop_block();
1952 printer->pop_block(
";");
1959 for (
auto& statement: read_statements) {
1960 printer->add_line(statement);
1964 if (node !=
nullptr) {
1966 print_statement_block(*block,
false,
false);
1971 for (
auto& statement: write_statements) {
1973 printer->add_line(text);
1978 if (info.mod_suffix ==
"nothing") {
1983 "_nrn_mechanism_cache_range _lmc{_sorted_token, *nt, *_ml_arg, _ml_arg->type()};");
1984 printer->fmt_line(
"auto inst = make_instance_{}(&_lmc);", info.mod_suffix);
1985 if (!info.artificial_cell) {
1986 printer->fmt_line(
"auto node_data = make_node_data_{}(*nt, *_ml_arg);", info.mod_suffix);
1988 printer->add_line(
"auto* _thread = _ml_arg->_thread;");
1989 if (!codegen_thread_variables.empty()) {
1990 printer->fmt_line(
"auto _thread_vars = {}(_thread[{}].get<double*>());",
1991 thread_variables_struct(),
1992 info.thread_var_thread_id);
1998 if (info.mod_suffix ==
"nothing") {
2002 printer->add_line(
"Datum* _ppvar = _nrn_mechanism_access_dparam(prop);");
2003 printer->add_line(
"_nrn_mechanism_cache_instance _lmc{prop};");
2004 printer->add_line(
"const size_t id = 0;");
2006 printer->fmt_line(
"auto inst = make_instance_{}(prop ? &_lmc : nullptr);", info.mod_suffix);
2007 if (!info.artificial_cell) {
2008 printer->fmt_line(
"auto node_data = make_node_data_{}(prop);", info.mod_suffix);
2011 if (!codegen_thread_variables.empty()) {
2012 printer->fmt_line(
"auto _thread_vars = {}({}_global.thread_data);",
2013 thread_variables_struct(),
2017 printer->add_newline();
2022 const std::string& function_name) {
2023 std::string method = function_name.empty() ? compute_method_name(type) : function_name;
2024 ParamVector args = {{
"",
"const _nrn_model_sorted_token&",
"",
"_sorted_token"},
2025 {
"",
"NrnThread*",
"",
"nt"},
2026 {
"",
"Memb_list*",
"",
"_ml_arg"},
2027 {
"",
"int",
"",
"_type"}};
2028 printer->fmt_push_block(
"static void {}({})", method, get_parameter_str(args));
2029 print_entrypoint_setup_code_from_memb_list();
2030 printer->add_line(
"auto nodecount = _ml_arg->nodecount;");
2035 printer->add_newline(2);
2039 printer->push_block(
"for (int id = 0; id < nodecount; id++)");
2041 printer->add_line(
"auto* _ppvar = _ml_arg->pdata[id];");
2042 if (!info.artificial_cell) {
2043 printer->add_line(
"int node_id = node_data.nodeindices[id];");
2044 printer->add_line(
"inst.v_unused[id] = node_data.node_voltages[node_id];");
2047 print_rename_state_vars();
2049 if (!info.changed_dt.empty()) {
2050 printer->fmt_line(
"double _save_prev_dt = {};",
2052 printer->fmt_line(
"{} = {};",
2057 print_initial_block(info.initial_node);
2059 if (!info.changed_dt.empty()) {
2063 printer->pop_block();
2064 printer->pop_block();
2068 printer->add_newline(2);
2070 ParamVector args = {{
"",
"const _nrn_model_sorted_token&",
"",
"_sorted_token"},
2071 {
"",
"NrnThread*",
"",
"nt"},
2072 {
"",
"Memb_list*",
"",
"_ml_arg"},
2073 {
"",
"int",
"",
"_type"}};
2075 printer->fmt_push_block(
"static void {}({})",
2077 get_parameter_str(args));
2080 print_entrypoint_setup_code_from_memb_list();
2081 printer->fmt_line(
"auto nodecount = _ml_arg->nodecount;");
2082 printer->push_block(
"for (int id = 0; id < nodecount; id++)");
2084 if (breakpoint_exist()) {
2085 printer->add_line(
"int node_id = node_data.nodeindices[id];");
2086 printer->fmt_line(
"node_data.node_diagonal[node_id] {} inst.{}[id];",
2092 printer->pop_block();
2093 printer->pop_block();
2098 if (info.constructor_node) {
2099 printer->fmt_line(
"static void {}(Prop* prop);",
2105 if (info.constructor_node) {
2106 printer->fmt_push_block(
"static void {}(Prop* prop)",
2109 print_entrypoint_setup_code_from_prop();
2111 auto block = info.constructor_node->get_statement_block();
2112 print_statement_block(*block,
false,
false);
2114 printer->pop_block();
2124 printer->fmt_push_block(
"static void {}(Prop* prop)",
2126 print_entrypoint_setup_code_from_prop();
2128 for (
const auto& rv: info.random_variables) {
2129 printer->fmt_line(
"nrnran123_deletestream((nrnran123_State*) {});",
2130 get_variable_name(
get_name(rv),
false));
2134 if (info.destructor_node) {
2135 auto block = info.destructor_node->get_statement_block();
2136 print_statement_block(*block,
false,
false);
2139 printer->pop_block();
2144 printer->add_newline(2);
2147 printer->fmt_push_block(
"static void {}(Prop* _prop)", method);
2148 printer->add_line(
"Datum *_ppvar = nullptr;");
2150 if (info.point_process) {
2151 printer->push_block(
"if (nrn_point_prop_)");
2152 printer->add_multi_line(R
"CODE(
2153 _nrn_mechanism_access_alloc_seq(_prop) = _nrn_mechanism_access_alloc_seq(nrn_point_prop_);
2154 _ppvar = _nrn_mechanism_access_dparam(nrn_point_prop_);
2156 printer->chain_block("else");
2158 if (info.semantic_variable_count || info.emit_cvode) {
2159 printer->fmt_line(
"_ppvar = nrn_prop_datum_alloc(mech_type, {}, _prop);",
2160 info.semantic_variable_count +
static_cast<int>(info.emit_cvode));
2161 printer->add_line(
"_nrn_mechanism_access_dparam(_prop) = _ppvar;");
2163 printer->add_multi_line(R
"CODE(
2164 _nrn_mechanism_cache_instance _lmc{_prop};
2165 size_t const _iml = 0;
2167 printer->fmt_line("assert(_nrn_mechanism_get_num_vars(_prop) == {});",
2168 codegen_float_variables.size());
2169 if (float_variables_size()) {
2170 printer->add_line(
"/*initialize range parameters*/");
2171 for (
size_t i_param = 0; i_param < info.range_parameter_vars.size(); ++i_param) {
2172 const auto var = info.range_parameter_vars[i_param];
2173 if (var->is_array()) {
2176 const auto& var_name = var->get_name();
2177 auto var_pos = position_of_float_var(var_name);
2179 printer->fmt_line(
"_lmc.template fpfield<{}>(_iml) = {}; /* {} */",
2181 fmt::format(
"_parameter_defaults[{}]", i_param),
2185 if (info.point_process) {
2186 printer->pop_block();
2189 if (info.semantic_variable_count) {
2190 printer->add_line(
"_nrn_mechanism_access_dparam(_prop) = _ppvar;");
2193 const auto codegen_int_variables_size = codegen_int_variables.size();
2195 if (info.diam_used || info.area_used) {
2196 for (
size_t i = 0; i < codegen_int_variables.size(); ++i) {
2197 auto var_info = codegen_int_variables[i];
2199 printer->fmt_line(
"Prop * morphology_prop = need_memb({}._morphology_sym);",
2200 global_struct_instance());
2202 "_ppvar[{}] = _nrn_mechanism_get_param_handle(morphology_prop, 0);", i);
2205 printer->fmt_line(
"_ppvar[{}] = _nrn_mechanism_get_area_handle(nrn_alloc_node_);",
2211 for (
const auto& ion: info.ions) {
2212 printer->fmt_line(
"Symbol * {}_sym = hoc_lookup(\"{}_ion\");", ion.name, ion.name);
2213 printer->fmt_line(
"Prop * {}_prop = need_memb({}_sym);", ion.name, ion.name);
2215 if (ion.is_exterior_conc_written()) {
2216 printer->fmt_line(
"nrn_check_conc_write(_prop, {}_prop, 0);", ion.name);
2219 if (ion.is_interior_conc_written()) {
2220 printer->fmt_line(
"nrn_check_conc_write(_prop, {}_prop, 1);", ion.name);
2223 int conc = ion.is_conc_written() ? 3 : int(ion.is_conc_read());
2224 int rev = ion.is_rev_written() ? 3 : int(ion.is_rev_read());
2226 printer->fmt_line(
"nrn_promote({}_prop, {}, {});", ion.name, conc, rev);
2228 for (
size_t i = 0; i < codegen_int_variables_size; ++i) {
2229 const auto& var = codegen_int_variables[i];
2231 const std::string& var_name = var.symbol->get_name();
2234 std::string ion_var_name = std::string(var_name.begin() + 4, var_name.end());
2235 if (ion.is_ionic_variable(ion_var_name) ||
2236 ion.is_current_derivative(ion_var_name) || ion.is_rev_potential(ion_var_name)) {
2237 printer->fmt_line(
"_ppvar[{}] = _nrn_mechanism_get_param_handle({}_prop, {});",
2240 ion.variable_index(ion_var_name));
2243 if (ion.is_style(var_name)) {
2245 "_ppvar[{}] = {{neuron::container::do_not_search, "
2246 "&(_nrn_mechanism_access_dparam({}_prop)[0].literal_value<int>())}};",
2254 if (!info.random_variables.empty()) {
2255 for (
const auto& rv: info.random_variables) {
2256 auto position = position_of_int_var(
get_name(rv));
2257 printer->fmt_line(
"_ppvar[{}].literal_value<void*>() = nrnran123_newstream();",
2260 printer->fmt_line(
"nrn_mech_inst_destruct[mech_type] = neuron::{};",
2264 if (info.point_process || info.artificial_cell) {
2265 printer->fmt_push_block(
"if(!nrn_point_prop_)");
2267 if (info.constructor_node) {
2270 printer->pop_block();
2273 printer->pop_block();
2282 if (!nrn_state_required()) {
2286 printer->add_newline(2);
2289 printer->push_block(
"for (int id = 0; id < nodecount; id++)");
2290 printer->add_line(
"int node_id = node_data.nodeindices[id];");
2291 printer->add_line(
"auto* _ppvar = _ml_arg->pdata[id];");
2292 if (!info.artificial_cell) {
2293 printer->add_line(
"inst.v_unused[id] = node_data.node_voltages[node_id];");
2300 if (ion_variable_struct_required()) {
2301 throw std::runtime_error(
"Not implemented.");
2305 for (
auto& statement: read_statements) {
2306 printer->add_line(statement);
2309 if (info.nrn_state_block) {
2310 info.nrn_state_block->visit_children(*
this);
2313 if (info.currents.empty() && info.breakpoint_node !=
nullptr) {
2314 auto block = info.breakpoint_node->get_statement_block();
2315 print_statement_block(*block,
false,
false);
2319 for (
auto& statement: write_statements) {
2320 const auto& text = process_shadow_update_statement(statement,
BlockType::State);
2321 printer->add_line(text);
2324 printer->pop_block();
2325 printer->pop_block();
2334 return get_arg_str(nrn_current_parameters());
2339 if (ion_variable_struct_required()) {
2340 throw std::runtime_error(
"Not implemented.");
2343 ParamVector params = {{
"",
"_nrn_mechanism_cache_range&",
"",
"_lmc"},
2344 {
"",
"NrnThread*",
"",
"nt"},
2345 {
"",
"Datum*",
"",
"_ppvar"},
2346 {
"",
"Datum*",
"",
"_thread"}};
2348 if (info.thread_callback_register) {
2349 auto type_name = fmt::format(
"{}&", thread_variables_struct());
2350 params.emplace_back(
"", type_name,
"",
"_thread_vars");
2352 params.emplace_back(
"",
"size_t",
"",
"id");
2353 params.emplace_back(
"", fmt::format(
"{}&", instance_struct()),
"",
"inst");
2354 params.emplace_back(
"", fmt::format(
"{}&", node_data_struct()),
"",
"node_data");
2355 params.emplace_back(
"",
"double",
"",
"v");
2361 const auto& args = nrn_current_parameters();
2363 printer->add_newline(2);
2364 printer->fmt_push_block(
"static inline double nrn_current_{}({})",
2366 get_parameter_str(args));
2367 printer->add_line(
"inst.v_unused[id] = v;");
2368 printer->add_line(
"double current = 0.0;");
2369 print_statement_block(*block,
false,
false);
2370 for (
auto& current: info.currents) {
2371 const auto& name = get_variable_name(current);
2372 printer->fmt_line(
"current += {};", name);
2374 printer->add_line(
"return current;");
2375 printer->pop_block();
2381 print_statement_block(*block,
false,
false);
2382 if (!info.currents.empty()) {
2384 for (
const auto& current: info.currents) {
2385 auto var = breakpoint_current(current);
2386 sum += get_variable_name(var);
2387 if (¤t != &info.currents.back()) {
2391 printer->fmt_line(
"double rhs = {};", sum);
2395 for (
const auto& conductance: info.conductances) {
2396 auto var = breakpoint_current(conductance.variable);
2397 sum += get_variable_name(var);
2398 if (&conductance != &info.conductances.back()) {
2402 printer->fmt_line(
"double g = {};", sum);
2404 for (
const auto& conductance: info.conductances) {
2405 if (!conductance.ion.empty()) {
2408 const auto& rhs = get_variable_name(conductance.variable);
2411 printer->add_line(text);
2418 printer->fmt_line(
"double I1 = nrn_current_{}({}+0.001);",
2420 nrn_current_arguments());
2421 for (
auto& ion: info.ions) {
2422 for (
auto& var: ion.writes) {
2423 if (ion.is_ionic_current(var)) {
2424 const auto& name = get_variable_name(var);
2425 printer->fmt_line(
"double di{} = {};", ion.name, name);
2429 printer->fmt_line(
"double I0 = nrn_current_{}({});", info.mod_suffix, nrn_current_arguments());
2430 printer->add_line(
"double rhs = I0;");
2432 printer->add_line(
"double g = (I1-I0)/0.001;");
2433 for (
auto& ion: info.ions) {
2434 for (
auto& var: ion.writes) {
2435 if (ion.is_ionic_current(var)) {
2437 auto rhs = fmt::format(
"(di{}-{})/0.001", ion.name, get_variable_name(var));
2438 if (info.point_process) {
2440 rhs += fmt::format(
"*1.e2/{}", area);
2444 printer->add_line(text);
2452 printer->add_line(
"int node_id = node_data.nodeindices[id];");
2453 printer->add_line(
"double v = node_data.node_voltages[node_id];");
2454 printer->add_line(
"auto* _ppvar = _ml_arg->pdata[id];");
2456 for (
auto& statement: read_statements) {
2457 printer->add_line(statement);
2460 if (info.conductances.empty()) {
2461 print_nrn_cur_non_conductance_kernel();
2463 print_nrn_cur_conductance_kernel(node);
2467 for (
auto& statement: write_statements) {
2469 printer->add_line(text);
2472 if (info.point_process) {
2474 printer->fmt_line(
"double mfactor = 1.e2/{};", area);
2475 printer->add_line(
"g = g*mfactor;");
2476 printer->add_line(
"rhs = rhs*mfactor;");
2491 if (!nrn_cur_required()) {
2495 if (info.conductances.empty()) {
2496 print_nrn_current(*info.breakpoint_node);
2499 printer->add_newline(2);
2500 printer->add_line(
"/** update current */");
2503 printer->push_block(
"for (int id = 0; id < nodecount; id++)");
2504 print_nrn_cur_kernel(*info.breakpoint_node);
2511 printer->fmt_line(
"node_data.node_rhs[node_id] {} rhs;", operator_for_rhs());
2513 if (breakpoint_exist()) {
2514 printer->fmt_line(
"inst.{}[id] = g;",
2518 printer->pop_block();
2528 printer->pop_block();
2537 print_standard_includes();
2538 print_neuron_includes();
2540 if (info.thread_callback_register) {
2541 printer->add_line(
"extern void _nrn_thread_reg(int, int, void(*)(Datum*));");
2547 print_global_macros();
2548 print_mechanism_variables_macros();
2550 printer->add_line(
"extern Node* nrn_alloc_node_;");
2555 printer->add_newline();
2556 printer->add_line(
"/* NEURON global macro definitions */");
2557 if (info.vectorize) {
2558 printer->add_multi_line(R
"CODE(
2560 #define NRN_VECTORIZED 1
2563 printer->add_multi_line(R
"CODE(
2564 /* NOT VECTORIZED */
2565 #define NRN_VECTORIZED 0
2572 printer->add_newline();
2573 printer->add_line(
"static constexpr auto number_of_datum_variables = ",
2576 printer->add_line(
"static constexpr auto number_of_floating_point_variables = ",
2579 printer->add_newline();
2580 printer->add_multi_line(R
"CODE(
2582 template <typename T>
2583 using _nrn_mechanism_std_vector = std::vector<T>;
2584 using _nrn_model_sorted_token = neuron::model_sorted_token;
2585 using _nrn_mechanism_cache_range = neuron::cache::MechanismRange<number_of_floating_point_variables, number_of_datum_variables>;
2586 using _nrn_mechanism_cache_instance = neuron::cache::MechanismInstance<number_of_floating_point_variables, number_of_datum_variables>;
2587 using _nrn_non_owning_id_without_container = neuron::container::non_owning_identifier_without_container;
2588 template <typename T>
2589 using _nrn_mechanism_field = neuron::mechanism::field<T>;
2590 template <typename... Args>
2591 void _nrn_mechanism_register_data_fields(Args&&... args) {
2592 neuron::mechanism::register_data_fields(std::forward<Args>(args)...);
2597 if (info.point_process) {
2598 printer->add_line(
"extern Prop* nrn_point_prop_;");
2600 printer->add_line(
"Prop* hoc_getdata_range(int type);");
2603 if (info.table_count > 0) {
2604 printer->add_line(
"void _nrn_thread_table_reg(int, nrn_thread_table_check_t);");
2607 printer->add_line(
"extern void _cvode_abstol(Symbol**, double*, int);");
2608 if (info.for_netcon_used) {
2609 printer->add_line(
"int _nrn_netcon_args(void*, double***);");
2615 print_mechanism_global_var_structure(print_initializers);
2616 print_mechanism_range_var_structure(print_initializers);
2617 print_node_data_structure(print_initializers);
2618 print_thread_variables_structure(print_initializers);
2619 print_make_instance();
2620 print_make_node_data();
2625 if (!info.vectorize) {
2628 printer->add_multi_line(R
"CODE(
2630 inst->v_unused[id] = v;
2637 printer->add_multi_line(R
"CODE(
2639 inst->g_unused[id] = g;
2645 print_hoc_py_wrapper_function_definitions();
2646 for (
const auto& procedure: info.procedures) {
2647 print_procedure(*procedure);
2649 for (
const auto&
function: info.functions) {
2650 print_function(*
function);
2652 for (
const auto& function_table: info.function_tables) {
2653 print_function_tables(*function_table);
2662 print_net_receive();
2667 print_backend_info();
2668 print_headers_include();
2669 print_macro_definitions();
2670 print_neuron_global_variable_declarations();
2671 print_namespace_start();
2672 print_nmodl_constants();
2673 print_prcellstate_macros();
2674 print_mechanism_info();
2675 print_data_structures(
true);
2676 print_nrn_constructor_declaration();
2677 print_nrn_destructor_declaration();
2679 print_function_prototypes();
2680 print_longitudinal_diffusion_callbacks();
2681 print_cvode_definitions();
2682 print_point_process_function_definitions();
2683 print_setdata_functions();
2684 print_check_table_entrypoint();
2685 print_top_verbatim_blocks();
2686 print_functors_definitions();
2687 print_global_variables_for_hoc();
2688 print_thread_memory_callbacks();
2689 print_function_definitions();
2690 print_compute_functions();
2691 print_nrn_constructor();
2692 print_nrn_destructor();
2693 print_sdlists_init(
true);
2694 print_mechanism_register();
2695 print_namespace_stop();
2699 print_backend_info();
2700 print_headers_include();
2701 print_namespace_start();
2702 print_function_prototypes();
2703 print_top_verbatim_blocks();
2704 print_global_variables_for_hoc();
2705 print_function_definitions();
2706 print_mechanism_register();
2707 print_namespace_stop();
2711 if (info.mod_suffix ==
"nothing") {
2712 print_codegen_routines_nothing();
2714 print_codegen_routines_regular();
2719 throw std::runtime_error(
"Not implemented.");
2726 if (printing_net_init) {
2727 throw std::runtime_error(
"Not implemented. [jfiwoei]");
2730 std::string weight_pointer =
"nullptr";
2733 if (!printing_net_receive) {
2734 point_process +=
".get<Point_process*>()";
2736 const auto& tqitem = get_variable_name(
"tqitem",
false);
2738 printer->fmt_text(
"{}(/* tqitem */ &{}, {}, {}, {} + ",
2739 info.artificial_cell ?
"artcell_net_send" :
"net_send",
2743 get_variable_name(
"t"));
2744 print_vector_elements(arguments,
", ");
2745 printer->add_text(
')');
2749 const auto& point_process = get_variable_name(
"point_process",
false);
2750 const auto& tqitem = get_variable_name(
"tqitem",
false);
2752 printer->fmt_text(
"{}(/* tqitem */ &{}, {}, ",
2753 info.artificial_cell ?
"artcell_net_move" :
"net_move",
2758 printer->add_text(
')');
2764 printer->fmt_text(
"net_event({}, t)", point_process);
2770 printer->add_text(method_name(name),
"(");
2771 print_vector_elements(arguments,
", ");
2772 printer->add_text(
')');
2796 auto n_parameters = parameters.size();
2797 for (
size_t i = 0; i < n_parameters; ++i) {
2798 const auto& name = parameters[i]->get_node_name();
2809 return {{
"",
"Point_process*",
"",
"_pnt"},
2810 {
"",
"double*",
"",
"_args"},
2811 {
"",
"double",
"",
"flag"}};
2816 printer->add_line(
"_nrn_mechanism_cache_instance _lmc{_pnt->prop};");
2817 printer->add_line(
"auto * nt = static_cast<NrnThread*>(_pnt->_vnt);");
2818 printer->add_line(
"auto * _ppvar = _nrn_mechanism_access_dparam(_pnt->prop);");
2820 printer->fmt_line(
"auto inst = make_instance_{}(&_lmc);", info.mod_suffix);
2821 if (!info.artificial_cell) {
2822 printer->fmt_line(
"auto node_data = make_node_data_{}(_pnt->prop);", info.mod_suffix);
2824 printer->fmt_line(
"// nocmodl has a nullptr dereference for thread variables.");
2825 printer->fmt_line(
"// NMODL will fail to compile at a later point, because of");
2826 printer->fmt_line(
"// missing '_thread_vars'.");
2827 printer->fmt_line(
"Datum * _thread = nullptr;");
2829 printer->add_line(
"size_t id = 0;");
2830 printer->add_line(
"double t = nt->_t;");
2834 return {{
"",
"const _nrn_model_sorted_token&",
"",
"_sorted_token"},
2835 {
"",
"NrnThread*",
"",
"nt"},
2836 {
"",
"Memb_list*",
"",
"_ml_arg"},
2837 {
"",
"int",
"",
"_type"}};
2841 ParamVector args = {{
"",
"_nrn_mechanism_cache_range&",
"",
"_lmc"},
2842 {
"", fmt::format(
"{}_Instance&", info.mod_suffix),
"",
"inst"},
2843 {
"", fmt::format(
"{}_NodeData&", info.mod_suffix),
"",
"node_data"},
2844 {
"",
"size_t",
"",
"id"},
2845 {
"",
"Datum*",
"",
"_ppvar"},
2846 {
"",
"Datum*",
"",
"_thread"},
2847 {
"",
"NrnThread*",
"",
"nt"}};
2849 if (info.thread_callback_register) {
2850 auto type_name = fmt::format(
"{}&", thread_variables_struct());
2851 args.emplace_back(
"", type_name,
"",
"_thread_vars");
2858 printer->fmt_push_block(
"static constexpr int {}(int _type)",
2860 printer->fmt_line(
"return {};", info.cvode_block->get_n_odes()->get_value());
2861 printer->pop_block();
2862 printer->add_newline(2);
2867 {
"",
"Prop*",
"",
"_prop"},
2868 {
"",
"int",
"",
"equation_index"},
2869 {
"",
"neuron::container::data_handle<double>*",
"",
"_pv"},
2870 {
"",
"neuron::container::data_handle<double>*",
"",
"_pvdot"},
2871 {
"",
"double*",
"",
"_atol"},
2872 {
"",
"int",
"",
"_type"}};
2874 auto get_param_name = [](
const auto& item) {
return std::get<3>(item); };
2876 auto prop_name = get_param_name(tolerances_parameters[0]);
2877 auto eqindex_name = get_param_name(tolerances_parameters[1]);
2878 auto pv_name = get_param_name(tolerances_parameters[2]);
2879 auto pvdot_name = get_param_name(tolerances_parameters[3]);
2880 auto atol_name = get_param_name(tolerances_parameters[4]);
2882 printer->fmt_push_block(
"static void {}({})",
2884 get_parameter_str(tolerances_parameters));
2885 printer->fmt_line(
"auto* _ppvar = _nrn_mechanism_access_dparam({});", prop_name);
2886 printer->fmt_line(
"_ppvar[{}].literal_value<int>() = {};", int_variables_size(), eqindex_name);
2887 printer->fmt_push_block(
"for (int i = 0; i < {}(0); i++)",
2889 printer->fmt_line(
"{}[i] = _nrn_mechanism_get_param_handle({}, _slist1[i]);",
2892 printer->fmt_line(
"{}[i] = _nrn_mechanism_get_param_handle({}, _dlist1[i]);",
2895 printer->fmt_line(
"_cvode_abstol(_atollist, {}, i);", atol_name);
2896 printer->pop_block();
2897 printer->pop_block();
2898 printer->add_newline(2);
2903 printer->fmt_push_block(
"static int {}({})",
2905 get_parameter_str(cvode_update_parameters()));
2907 "auto v = node_data.node_voltages ? "
2908 "node_data.node_voltages[node_data.nodeindices[id]] : 0.0;");
2910 print_statement_block(block,
false,
false);
2912 printer->add_line(
"return 0;");
2913 printer->pop_block();
2914 printer->add_newline(2);
2918 const std::string& update_name) {
2919 printer->fmt_push_block(
"static void {}({})",
2920 method_name(setup_name),
2921 get_parameter_str(cvode_setup_parameters()));
2922 print_entrypoint_setup_code_from_memb_list();
2923 printer->fmt_line(
"auto nodecount = _ml_arg->nodecount;");
2924 printer->push_block(
"for (int id = 0; id < nodecount; id++)");
2925 printer->add_line(
"auto* _ppvar = _ml_arg->pdata[id];");
2927 "auto v = node_data.node_voltages ? "
2928 "node_data.node_voltages[node_data.nodeindices[id]] : 0.0;");
2929 printer->fmt_line(
"{}({});", method_name(update_name), get_arg_str(cvode_update_parameters()));
2931 printer->pop_block();
2932 printer->pop_block();
2934 printer->add_newline(2);
2938 if (!info.emit_cvode) {
2942 printer->add_newline(2);
2943 printer->add_line(
"/* Functions related to CVODE codegen */");
2944 print_cvode_count();
2945 print_cvode_tolerances();
2947 *info.cvode_block->get_non_stiff_block());
2954 printing_net_receive =
true;
2955 auto node = info.net_receive_node;
2960 printer->fmt_push_block(
"static void nrn_net_receive_{}({})",
2962 get_parameter_str(net_receive_args()));
2965 print_net_receive_common_code();
2968 print_statement_block(*node->get_statement_block(),
false,
false);
2970 printer->add_newline();
2971 printer->pop_block();
2972 printing_net_receive =
false;
2976 const auto node = info.net_receive_initial_node;
2977 if (node ==
nullptr) {
2984 printing_net_init =
true;
2985 printer->add_newline(2);
2986 printer->fmt_push_block(
"static void net_init({})", get_parameter_str(net_receive_args()));
2988 auto block = node->get_statement_block().get();
2989 if (!block->get_statements().empty()) {
2990 print_net_receive_common_code();
2991 print_statement_block(*block,
false,
false);
2993 printer->pop_block();
2994 printing_net_init =
false;
3035 for (
size_t i_arg = 0; i_arg < args.size(); ++i_arg) {
3036 auto old_name = args[i_arg]->get_node_name();
3037 auto new_name = fmt::format(
"_netcon_data[{}]", i_arg);
3038 v.
set(old_name, new_name);
3039 statement_block->accept(v);
3043 std::find_if(info.semantics.begin(), info.semantics.end(), [](
const IndexSemantics& a) {
3044 return a.name == naming::FOR_NETCON_SEMANTIC;
3046 if (dparam_it == info.semantics.end()) {
3047 throw std::runtime_error(
"Couldn't find `fornetcon` variable.");
3050 int dparam_index = dparam_it->index;
3051 auto netcon_var =
get_name(codegen_int_variables[dparam_index]);
3055 printer->add_text(
"double ** _fornetcon_data;");
3056 printer->add_newline();
3058 printer->fmt_line(
"int _n_netcons = _nrn_netcon_args({}, &_fornetcon_data);",
3059 get_variable_name(netcon_var,
false));
3061 printer->push_block(
"for (size_t _i = 0; _i < _n_netcons; ++_i)");
3062 printer->add_line(
"double * _netcon_data = _fornetcon_data[_i];");
3063 print_statement_block(*statement_block,
false,
false);
3064 printer->pop_block();
3068 printer->add_line(
"_NMODLMUTEXLOCK");
3069 printer->add_indent();
3071 printer->add_text(
";");
3072 printer->add_line(
"_NMODLMUTEXUNLOCK");