Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2057f13
Initial commit for include statement
pnbabu Sep 26, 2024
2563e63
run context condition checks only once, after model parsing
Sep 29, 2024
bbc5509
Merge remote-tracking branch 'upstream/master' into symboltable_checks
Oct 4, 2024
fcffa1e
Merge remote-tracking branch 'upstream/master' into symboltable_checks
Oct 7, 2024
5f93cdb
Merge remote-tracking branch 'clinssen/symboltable_checks' into nestm…
Oct 7, 2024
3bae190
run context condition checks only once, after model parsing
Oct 7, 2024
ac766f0
Merge remote-tracking branch 'clinssen/symboltable_checks' into nestm…
Oct 7, 2024
eecc955
add include statement
Oct 7, 2024
a377d19
Generate parser files
pnbabu Oct 7, 2024
ff6d3d4
add include statement
Oct 7, 2024
84a577d
Merge remote-tracking branch 'upstream/master' into nestml_include
Oct 11, 2024
5b2865f
add include statement
Oct 11, 2024
961953d
Merge remote-tracking branch 'upstream/master' into nestml_include
Nov 22, 2024
20255b3
add include statement
Nov 22, 2024
e3abec8
add include statement
Nov 22, 2024
0726cd6
add include statement
Nov 25, 2024
229a3a6
add include statement
Nov 26, 2024
7b3f127
clean up NESTML grammar definition
Nov 28, 2024
ac188a6
add include statement
Nov 28, 2024
d041b62
Merge remote-tracking branch 'clinssen/grammar_cleanup' into nestml_i…
Nov 28, 2024
16e9913
add include statement
Nov 28, 2024
8076f2c
add include statement
Nov 30, 2024
f8052c7
add include statement
Nov 30, 2024
bb8ad89
Merge remote-tracking branch 'upstream/master' into nestml_include
Dec 2, 2024
5dfd3fc
add include statement
Dec 2, 2024
93a654a
Merge remote-tracking branch 'upstream/master' into nestml_include
Dec 2, 2024
03bb7da
add include statement
Dec 2, 2024
d89fa28
add include statement
Dec 3, 2024
bd3c03f
add include statement
Dec 3, 2024
f00fdb7
Include file with multiple blocks
pnbabu Dec 4, 2024
d1b64b5
Merge remote-tracking branch 'upstream/master' into nestml_include
pnbabu Dec 4, 2024
a963e28
Include statement with multiple blocks
pnbabu Dec 5, 2024
a176778
Add include statement with multiple blocks and statements
pnbabu Dec 9, 2024
cc5e187
use single-line comment characters for docstrings
Feb 13, 2025
5b72684
Merge remote-tracking branch 'upstream/master' into nestml_include
Feb 19, 2025
00a7ec3
Merge remote-tracking branch 'clinssen/docstring' into nestml_include
Feb 19, 2025
ef8a819
add include statement
Feb 19, 2025
86b790e
Merge remote-tracking branch 'upstream/master' into nestml_include
Mar 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions doc/nestml_language/nestml_language_concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,46 @@ The numeric results of a typical simulation run are shown below. Consider a leak
On the left, both pre-synaptic spikes are only processed at the end of the interval in which they occur. The statements in the ``update`` block are run every timestep for a fixed timestep of :math:`1~\text{ms}`, alternating with the statements in the ``onReceive`` handler for the spiking input port. Note that this means that the effect of the spikes becomes visible at the end of the timestep in :math:`I_\text{syn}`, but it takes another timestep before ``integrate_odes()`` is called again and consequently for the effect of the spikes to become visible in the membrane potential. This results in a threshold crossing and the neuron firing a spike. On the right half of the figure, the same presynaptic spike timing is used, but events are processed at their exact time of occurrence. In this case, the ``update`` statements are called once to update the neuron from time 0 to :math:`1~\text{ms}`, then again to update from :math:`1~\text{ms}` to the time of the first spike, then the spike is processed by running the statements in its ``onReceive`` block, then ``update`` is called to update from the time of the first spike to the second spike, and so on. The time courses of :math:`I_\text{syn}` and :math:`V_\text{m}` are such that the threshold is not reached and the neuron does not fire, illustrating the numerical differences that can occur when the same model is simulated using different strategies.


Including files
---------------

To make models more modular, the contents of one NESTML file can be "included" into another, akin to the C ``#include`` directive. In NESTML, ``include`` is a statement taking one parameter, which gives the filename of the file to be included. The filename may include an absolute or relative path, with directories separated by a forward slash. Contents of the included file are interpreted as appearing at the point of the include statement (akin to the C ``#include``), but with indentation automatically adjusted to the indentation level of the ``include`` statement itself.

For example, if the contents of a to-be included file ``includes/my_equations_block.nestml`` are

.. code-block:: nestml

equations:
x' = A

then this file can be included as follows:

.. code-block:: nestml

model my_neuron_model:
include "includes/my_equations_block.nestml"

The include statement can appear at any indentation level, for example, if the file ``my_included_stmts.nestml`` contains a list of statements:

.. code-block:: nestml

println(i)
# check i
if i < 21:
i += 5
else:
i *= 2

then these can be included as follows:

.. code-block:: nestml

model my_neuron_model:
update:
while i < 42:
include "my_included_smts.nestml"


Guards
------

Expand Down
2 changes: 1 addition & 1 deletion doc/tutorials/izhikevich/izhikevich_task.nestml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# izhikevich_tutorial_neuron
# ##########################
# ##########################
#
#
# Copyright statement
Expand Down
3 changes: 1 addition & 2 deletions pynestml/cocos/co_co_all_variables_defined.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ def check_co_co(cls, node: ASTModel):
for equations_block in node.get_equations_blocks():
inline_expr_names.extend([inline_expr.variable_name for inline_expr in equations_block.get_inline_expressions()])
inline_exprs.extend(equations_block.get_inline_expressions())

if var.get_name() in inline_expr_names:
inline_expr_idx = inline_expr_names.index(var.get_name())
inline_expr = inline_exprs[inline_expr_idx]
Expand All @@ -78,7 +77,6 @@ def check_co_co(cls, node: ASTModel):
# actually, no problem detected, skip error
# XXX: TODO: check that differential order is less than or equal to that of the kernel
continue

# check if this symbol is actually a type, e.g. "mV" in the expression "(1 + 2) * mV"
symbol2 = var.get_scope().resolve_to_symbol(var.get_complete_name(), SymbolKind.TYPE)
if symbol2 is not None:
Expand Down Expand Up @@ -115,6 +113,7 @@ def check_co_co(cls, node: ASTModel):
# now check that they are not defined recursively, e.g. V_m mV = V_m + 1
# todo: we should not check this for invariants
if (symbol.get_referenced_object().get_source_position().encloses(var.get_source_position())
and not symbol.get_referenced_object().get_source_position().included_file
and not symbol.get_referenced_object().get_source_position().is_added_source_position()):
code, message = Messages.get_variable_defined_recursively(var.get_name())
Logger.log_message(code=code, message=message, error_position=symbol.get_referenced_object().
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see <http://www.gnu.org/licenses/>.

from pynestml.cocos.co_co import CoCo
from pynestml.meta_model.ast_assignment import ASTAssignment
from pynestml.meta_model.ast_model import ASTModel
from pynestml.cocos.co_co import CoCo
from pynestml.symbol_table.scope import ScopeType
from pynestml.symbols.symbol import SymbolKind
from pynestml.symbols.variable_symbol import BlockType
from pynestml.utils.ast_utils import ASTUtils
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.messages import Messages
Expand Down
4 changes: 2 additions & 2 deletions pynestml/cocos/co_cos_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ def check_cocos(cls, model: ASTModel, after_ast_rewrite: bool = False):
cls.check_variables_unique_in_scope(model)
cls.check_inline_expression_not_assigned_to(model)
cls.check_state_variables_initialized(model)
cls.check_variables_defined_before_usage(model)
if FrontendConfiguration.get_target_platform().upper() == 'NEST_COMPARTMENTAL':
# XXX: TODO: refactor this out; define a ``cocos_from_target_name()`` in the frontend instead.
cls.check_v_comp_requirement(model)
Expand All @@ -461,19 +460,20 @@ def check_cocos(cls, model: ASTModel, after_ast_rewrite: bool = False):
cls.check_integrate_odes_params_correct(model)
cls.check_output_port_defined_if_emit_call(model)
if not after_ast_rewrite:
cls.check_variables_defined_before_usage(model)
# units might be incorrect due to e.g. refactoring convolve call (Real type assigned)
cls.check_odes_have_consistent_units(model)
# ODE functions have been removed at this point
cls.check_function_declared_and_correctly_typed(model)
cls.check_ode_functions_have_consistent_units(model)
cls.check_correct_usage_of_kernels(model)
cls.check_resolution_func_used(model) # ``__h = resolution()`` is added after transformations; put this check inside the ``if`` to make sure it's not always triggered
cls.check_expression_correct(model)
if FrontendConfiguration.get_target_platform().upper() != 'NEST_COMPARTMENTAL':
cls.check_integrate_odes_called_if_equations_defined(model)
cls.check_invariant_type_correct(model)
cls.check_vector_in_non_vector_declaration_detected(model)
cls.check_convolve_has_correct_parameter(model)
cls.check_expression_correct(model)
cls.check_simple_delta_function(model)
cls.check_function_argument_template_types_consistent(model)
cls.check_vector_parameter_declaration(model)
Expand Down
7 changes: 7 additions & 0 deletions pynestml/codegeneration/printers/model_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from pynestml.meta_model.ast_function import ASTFunction
from pynestml.meta_model.ast_if_clause import ASTIfClause
from pynestml.meta_model.ast_if_stmt import ASTIfStmt
from pynestml.meta_model.ast_include_stmt import ASTIncludeStmt
from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
from pynestml.meta_model.ast_kernel import ASTKernel
Expand Down Expand Up @@ -159,6 +160,9 @@ def print_parameter(self, node: ASTParameter) -> str:
def print_return_stmt(self, node: ASTReturnStmt) -> str:
raise Exception("Printer does not support printing this node type")

def print_include_stmt(self, node: ASTIncludeStmt) -> str:
raise Exception("Printer does not support printing this node type")

def print_small_stmt(self, node: ASTSmallStmt) -> str:
raise Exception("Printer does not support printing this node type")

Expand Down Expand Up @@ -286,6 +290,9 @@ def print(self, node: ASTNode) -> str:
if isinstance(node, ASTReturnStmt):
return self.print_return_stmt(node)

if isinstance(node, ASTIncludeStmt):
return self.print_include_stmt(node)

if isinstance(node, ASTSimpleExpression):
return self.print_simple_expression(node)

Expand Down
22 changes: 16 additions & 6 deletions pynestml/codegeneration/printers/nestml_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from pynestml.meta_model.ast_function_call import ASTFunctionCall
from pynestml.meta_model.ast_if_clause import ASTIfClause
from pynestml.meta_model.ast_if_stmt import ASTIfStmt
from pynestml.meta_model.ast_include_stmt import ASTIncludeStmt
from pynestml.meta_model.ast_input_block import ASTInputBlock
from pynestml.meta_model.ast_input_port import ASTInputPort
from pynestml.meta_model.ast_input_qualifier import ASTInputQualifier
Expand Down Expand Up @@ -76,12 +77,13 @@ def __init__(self):

def print_model(self, node: ASTModel) -> str:
ret = print_ml_comments(node.pre_comments, self.indent, False)
ret += "model " + node.get_name() + ":" + print_sl_comment(node.in_comment)
ret += "\n" + self.print(node.get_body())

ret += "model " + node.get_name() + ":" + print_sl_comment(node.in_comment) + "\n"
self.inc_indent()
ret += self.print(node.get_body())
self.dec_indent()
return ret

def print_arithmetic_operator(celf, node: ASTArithmeticOperator) -> str:
def print_arithmetic_operator(self, node: ASTArithmeticOperator) -> str:
if node.is_times_op:
return " * "

Expand Down Expand Up @@ -168,12 +170,12 @@ def print_block_with_variables(self, node: ASTBlockWithVariables) -> str:
return ret

def print_model_body(self, node: ASTModelBody) -> str:
self.inc_indent()
# self.inc_indent()
ret = ""
for elem in node.body_elements:
ret += self.print(elem)

self.dec_indent()
# self.dec_indent()

return ret

Expand Down Expand Up @@ -475,6 +477,11 @@ def print_return_stmt(self, node: ASTReturnStmt):
ret += "return " + (self.print(node.get_expression()) if node.has_expression() else "")
return ret

def print_include_stmt(self, node: ASTIncludeStmt):
ret = print_n_spaces(self.indent)
ret += "include " + node.get_filename()
return ret

def print_simple_expression(self, node: ASTSimpleExpression) -> str:
if node.is_function_call():
return self.print(node.function_call)
Expand Down Expand Up @@ -513,7 +520,10 @@ def print_small_stmt(self, node: ASTSmallStmt) -> str:
ret += print_sl_comment(node.in_comment) + "\n"
elif node.is_declaration():
ret = self.print(node.get_declaration())
elif node.is_include_stmt():
ret = self.print(node.get_include_stmt())
else:
assert node.is_return_stmt()
ret = self.print(node.get_return_stmt())
return ret

Expand Down
20 changes: 16 additions & 4 deletions pynestml/frontend/pynestml_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
from pynestml.utils.logger import Logger, LoggingLevel
from pynestml.utils.messages import Messages
from pynestml.utils.model_parser import ModelParser
from pynestml.visitors.assign_implicit_conversion_factors_visitor import AssignImplicitConversionFactorsVisitor
from pynestml.visitors.ast_include_statement_visitor import ASTIncludeStatementVisitor
from pynestml.visitors.ast_parent_visitor import ASTParentVisitor
from pynestml.visitors.ast_symbol_table_visitor import ASTSymbolTableVisitor

Expand Down Expand Up @@ -411,7 +413,7 @@ def main() -> int:

def get_parsed_models() -> List[ASTModel]:
r"""
Handle the parsing and validation of the NESTML files
Handle the parsing and validation of the NESTML files

Returns
-------
Expand Down Expand Up @@ -442,6 +444,17 @@ def get_parsed_models() -> List[ASTModel]:
CoCosManager.check_model_names_unique(compilation_unit)
models.extend(compilation_unit.get_model_list())

# swap include statements for included file
for model in models:
model.accept(ASTIncludeStatementVisitor(os.path.dirname(model.file_path)))
model.accept(ASTSymbolTableVisitor())
print("MODEL AFTER INCLUDES REPLACED:")
print(model)

# .......
for model in models:
model.accept(AssignImplicitConversionFactorsVisitor())

# check that no models with duplicate names have been defined
CoCosManager.check_no_duplicate_compilation_unit_names(models)

Expand Down Expand Up @@ -491,15 +504,14 @@ def process() -> bool:
# validation -- check cocos for models that do not have errors already
excluded_models = []
for model in models:
if not Logger.has_errors(model.name):
CoCosManager.check_cocos(model)

if Logger.has_errors(model.name):
code, message = Messages.get_model_contains_errors(model.get_name())
Logger.log_message(node=model, code=code, message=message,
error_position=model.get_source_position(),
log_level=LoggingLevel.WARNING)
excluded_models.append(model)
else:
CoCosManager.check_cocos(model)

# exclude models that have errors
models = list(set(models) - set(excluded_models))
Expand Down
Loading
Loading