From 4ce1bc78d0d307c765ffd8e7dace11ea6832af10 Mon Sep 17 00:00:00 2001
From: Pooja Babu
Date: Wed, 15 Oct 2025 12:42:31 +0200
Subject: [PATCH 1/2] Fix synapse bug when the model uses both third factor and
neuromodulated spikes
---
.../common/SynapseHeader.h.jinja2 | 39 ++++++++++++++++---
1 file changed, 33 insertions(+), 6 deletions(-)
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2
index b3cd2142b..17bf7cdab 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2
@@ -1027,9 +1027,11 @@ void
{%- filter indent(4, True) %}
{%- set dynamics = synapse.get_on_receive_block(vt_port) %}
-{%- with ast = dynamics.get_stmts_body() %}
-{%- include "directives_cpp/StmtsBody.jinja2" %}
-{%- endwith %}
+{%- if dynamics is not none %}
+{%- with ast = dynamics.get_stmts_body() %}
+{%- include "directives_cpp/StmtsBody.jinja2" %}
+{%- endwith %}
+{%- endif %}
{%- endfilter %}
// process remaining dopa spikes in (t0, t1]
double cd;
@@ -1047,9 +1049,11 @@ void
**/
{%- filter indent(6, True) %}
{%- set dynamics = synapse.get_on_receive_block(vt_port) %}
-{%- with ast = dynamics.get_stmts_body() %}
-{%- include "directives_cpp/StmtsBody.jinja2" %}
-{%- endwith %}
+{%- if dynamics is not none %}
+{%- with ast = dynamics.get_stmts_body() %}
+{%- include "directives_cpp/StmtsBody.jinja2" %}
+{%- endwith %}
+{%- endif %}
{%- endfilter %}
/**
@@ -1398,6 +1402,16 @@ inline void
std::deque< histentry__{{ paired_neuron_name }} >::iterator start;
std::deque< histentry__{{ paired_neuron_name }} >::iterator finish;
static_cast<{{ paired_neuron_name }}*>(get_target(t))->get_history__( t_last_update_ - dendritic_delay, t_trig - dendritic_delay, &start, &finish );
+{%- if paired_neuron_name is not none and paired_neuron_name|length > 0 and paired_neuron.state_vars_that_need_continuous_buffering | length > 0 and continuous_state_buffering_method == "continuous_time_buffer" %}
+ // get continuous-time history entries in relevant range (t1, t2] from post-synaptic neuron
+ std::deque< continuous_variable_histentry_{{ paired_neuron_name }} >::iterator continuous_history_start;
+ std::deque< continuous_variable_histentry_{{ paired_neuron_name }} >::iterator continuous_history_finish;
+
+ ((post_neuron_t*)(get_target(t)))->get_continuous_variable_history( t_last_update_,
+ t_trig,
+ &continuous_history_start,
+ &continuous_history_finish );
+{%- endif %}
// facilitation due to postsyn. spikes since last update
double t0 = t_last_update_;
@@ -1415,6 +1429,19 @@ inline void
std::cout << "[synapse " << this << "] {{ synapseName }}: processing post spike from " << t_last_update_ << " to " << start->t_ + dendritic_delay << std::endl;
#endif
+{%- if paired_neuron_name is not none and paired_neuron_name|length > 0 and paired_neuron.state_vars_that_need_continuous_buffering | length > 0 %}
+ continuous_variable_histentry_{{ paired_neuron_name }} histentry(0.,
+{%- for state_var in paired_neuron.state_vars_that_need_continuous_buffering %}
+ {{ state_vars_that_need_continuous_buffering_transformed_iv[state_var] }}{% if not loop.last %},{% endif %}
+{%- endfor %});
+ get_entry_from_continuous_variable_history(start->t_ + dendritic_delay, continuous_history_start, continuous_history_finish, histentry);
+
+{%- for var_name in paired_neuron.state_vars_that_need_continuous_buffering %}
+{%- set var = utils.get_parameter_variable_by_name(astnode, var_name) %}
+ const double __{{ var_name }} = histentry.{{ var_name }};
+{%- endfor %}
+{%- endif %}
+
/**
* update synapse internal state from `t_last_update_` to `start->t_ + dendritic_delay`
**/
From 81e88aaf3a9cb96f0c0dd261a8b0e8fef5499645 Mon Sep 17 00:00:00 2001
From: "C.A.P. Linssen"
Date: Wed, 15 Oct 2025 13:05:29 +0200
Subject: [PATCH 2/2] slight refactoring
---
.../common/SynapseHeader.h.jinja2 | 31 ++++++++++++++-----
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2 b/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2
index 17bf7cdab..c11f82811 100644
--- a/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2
+++ b/pynestml/codegeneration/resources_nest/point_neuron/common/SynapseHeader.h.jinja2
@@ -1402,8 +1402,14 @@ inline void
std::deque< histentry__{{ paired_neuron_name }} >::iterator start;
std::deque< histentry__{{ paired_neuron_name }} >::iterator finish;
static_cast<{{ paired_neuron_name }}*>(get_target(t))->get_history__( t_last_update_ - dendritic_delay, t_trig - dendritic_delay, &start, &finish );
-{%- if paired_neuron_name is not none and paired_neuron_name|length > 0 and paired_neuron.state_vars_that_need_continuous_buffering | length > 0 and continuous_state_buffering_method == "continuous_time_buffer" %}
- // get continuous-time history entries in relevant range (t1, t2] from post-synaptic neuron
+
+{%- if paired_neuron_name is not none and paired_neuron_name|length > 0 and paired_neuron.state_vars_that_need_continuous_buffering | length > 0 %}
+
+{%- if continuous_state_buffering_method != "continuous_time_buffer" %}
+{{ raise('In combination with a volume transmitter input port, only ``"continuous_time_buffer"`` is supported for ``continuous_state_buffering_method`` code generator option.') }}
+{%- endif %}
+
+ // get continuous-time history entries in relevant range (t_last_update, t_trig] from post-synaptic neuron
std::deque< continuous_variable_histentry_{{ paired_neuron_name }} >::iterator continuous_history_start;
std::deque< continuous_variable_histentry_{{ paired_neuron_name }} >::iterator continuous_history_finish;
@@ -1421,7 +1427,7 @@ inline void
while ( start != finish )
{
{%- for vt_port in vt_ports %}
-{%- set vt_port = vt_ports[0] %}
+{%- set vt_port = vt_ports[0] %}
process_{{vt_port}}_spikes_( vt_spikes, t0, start->t_ + dendritic_delay, cp );
{%- endfor %}
@@ -1430,16 +1436,25 @@ inline void
#endif
{%- if paired_neuron_name is not none and paired_neuron_name|length > 0 and paired_neuron.state_vars_that_need_continuous_buffering | length > 0 %}
+
+{%- if continuous_state_buffering_method != "continuous_time_buffer" %}
+{{ raise('In combination with a volume transmitter input port, only ``"continuous_time_buffer"`` is supported for ``continuous_state_buffering_method`` code generator option.') }}
+{%- endif %}
+
+#ifdef DEBUG
+ std::cout << "Grabbing continuous_variable_history at t = " << start->t_ + dendritic_delay << "\n";
+#endif
+
continuous_variable_histentry_{{ paired_neuron_name }} histentry(0.,
{%- for state_var in paired_neuron.state_vars_that_need_continuous_buffering %}
{{ state_vars_that_need_continuous_buffering_transformed_iv[state_var] }}{% if not loop.last %},{% endif %}
-{%- endfor %});
+{%- endfor %});
get_entry_from_continuous_variable_history(start->t_ + dendritic_delay, continuous_history_start, continuous_history_finish, histentry);
-{%- for var_name in paired_neuron.state_vars_that_need_continuous_buffering %}
-{%- set var = utils.get_parameter_variable_by_name(astnode, var_name) %}
- const double __{{ var_name }} = histentry.{{ var_name }};
-{%- endfor %}
+{%- for var_name in paired_neuron.state_vars_that_need_continuous_buffering %}
+{%- set var = utils.get_parameter_variable_by_name(astnode, var_name) %}
+ const double __{{ var_name }} = histentry.{{ var_name }};
+{%- endfor %}
{%- endif %}
/**