def add_timestep_symbol(cls, neuron: ASTNeuron) -> None: """ Add timestep variable to the internals block """ assert neuron.get_initial_value( "__h" ) is None, "\"__h\" is a reserved name, please do not use variables by this name in your NESTML file" assert not "__h" in [ sym.name for sym in neuron.get_internal_symbols() ], "\"__h\" is a reserved name, please do not use variables by this name in your NESTML file" neuron.add_to_internal_block( ModelParser.parse_declaration('__h ms = resolution()'), index=0)
def transform_ode_and_kernels_to_json(self, neuron: ASTNeuron, parameters_block, kernel_buffers): """ Converts AST node to a JSON representation suitable for passing to ode-toolbox. Each kernel has to be generated for each spike buffer convolve in which it occurs, e.g. if the NESTML model code contains the statements convolve(G, ex_spikes) convolve(G, in_spikes) then `kernel_buffers` will contain the pairs `(G, ex_spikes)` and `(G, in_spikes)`, from which two ODEs will be generated, with dynamical state (variable) names `G__X__ex_spikes` and `G__X__in_spikes`. :param equations_block: ASTEquationsBlock :return: Dict """ odetoolbox_indict = {} gsl_converter = ODEToolboxReferenceConverter() gsl_printer = UnitlessExpressionPrinter(gsl_converter) odetoolbox_indict["dynamics"] = [] equations_block = neuron.get_equations_block() for equation in equations_block.get_ode_equations(): # n.b. includes single quotation marks to indicate differential order lhs = to_ode_toolbox_name(equation.get_lhs().get_complete_name()) rhs = gsl_printer.print_expression(equation.get_rhs()) entry = {"expression": lhs + " = " + rhs} symbol_name = equation.get_lhs().get_name() symbol = equations_block.get_scope().resolve_to_symbol( symbol_name, SymbolKind.VARIABLE) entry["initial_values"] = {} symbol_order = equation.get_lhs().get_differential_order() for order in range(symbol_order): iv_symbol_name = symbol_name + "'" * order initial_value_expr = neuron.get_initial_value(iv_symbol_name) if initial_value_expr: expr = gsl_printer.print_expression(initial_value_expr) entry["initial_values"][to_ode_toolbox_name( iv_symbol_name)] = expr odetoolbox_indict["dynamics"].append(entry) # write a copy for each (kernel, spike buffer) combination for kernel, spike_input_port in kernel_buffers: if is_delta_kernel(kernel): # delta function -- skip passing this to ode-toolbox continue for kernel_var in kernel.get_variables(): expr = get_expr_from_kernel_var(kernel, kernel_var.get_complete_name()) kernel_order = kernel_var.get_differential_order() kernel_X_spike_buf_name_ticks = construct_kernel_X_spike_buf_name( kernel_var.get_name(), spike_input_port, kernel_order, diff_order_symbol="'") replace_rhs_variables(expr, kernel_buffers) entry = {} entry[ "expression"] = kernel_X_spike_buf_name_ticks + " = " + str( expr) # initial values need to be declared for order 1 up to kernel order (e.g. none for kernel function f(t) = ...; 1 for kernel ODE f'(t) = ...; 2 for f''(t) = ... and so on) entry["initial_values"] = {} for order in range(kernel_order): iv_sym_name_ode_toolbox = construct_kernel_X_spike_buf_name( kernel_var.get_name(), spike_input_port, order, diff_order_symbol="'") symbol_name_ = kernel_var.get_name() + "'" * order symbol = equations_block.get_scope().resolve_to_symbol( symbol_name_, SymbolKind.VARIABLE) assert symbol is not None, "Could not find initial value for variable " + symbol_name_ initial_value_expr = symbol.get_declaring_expression() assert initial_value_expr is not None, "No initial value found for variable name " + symbol_name_ entry["initial_values"][ iv_sym_name_ode_toolbox] = gsl_printer.print_expression( initial_value_expr) odetoolbox_indict["dynamics"].append(entry) odetoolbox_indict["parameters"] = {} if parameters_block is not None: for decl in parameters_block.get_declarations(): for var in decl.variables: odetoolbox_indict["parameters"][var.get_complete_name( )] = gsl_printer.print_expression(decl.get_expression()) return odetoolbox_indict