Skip to content

Commit 3dd5ccd

Browse files
authored
Merge pull request #262 from NeuroML/feat/sim-meta
Expose `meta` in LEMSSimulation
2 parents a08ac71 + bbb2e2b commit 3dd5ccd

File tree

3 files changed

+112
-22
lines changed

3 files changed

+112
-22
lines changed

pyneuroml/lems/LEMSSimulation.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ def __init__(
3434
sim_id: str,
3535
duration: float,
3636
dt: float,
37-
target: str = None,
37+
target: typing.Optional[str] = None,
3838
comment: str = "\n\n This LEMS file has been automatically generated using PyNeuroML v%s (libNeuroML v%s)\n\n "
3939
% (pynml_ver, libnml_ver),
4040
lems_file_generate_seed: typing.Any = None,
4141
simulation_seed: int = 12345,
42+
meta: typing.Optional[typing.Dict[str, str]] = None,
4243
) -> None:
4344
"""Init a new LEMSSimulation object.
4445
@@ -62,6 +63,18 @@ def __init__(
6263
:type lems_file_generate_seed:
6364
:param simulation_seed: simulation seed to set to
6465
:type simulation_seed: int
66+
:param meta: dictionary to set Meta options.
67+
68+
Currently, only supported for the Neuron simulator to use the CVODE
69+
solver. A dict needs to be passed:
70+
{
71+
"for": "neuron",
72+
"method": "cvode",
73+
"abs_tolerance" = "0.001",
74+
"rel_tolerance": "0.001"
75+
}
76+
77+
:type meta: dict
6578
"""
6679

6780
self.lems_info["sim_id"] = sim_id
@@ -75,6 +88,7 @@ def __init__(
7588
self.lems_info["displays"] = []
7689
self.lems_info["output_files"] = []
7790
self.lems_info["event_output_files"] = []
91+
self.lems_info["meta"] = meta
7892

7993
if target:
8094
self.lems_info["target"] = target
@@ -251,7 +265,7 @@ def add_line_to_display(
251265
line_id: str,
252266
quantity: str,
253267
scale: str = "1",
254-
color: str = None,
268+
color: typing.Optional[str] = None,
255269
timeScale: str = "1ms",
256270
) -> None:
257271
"""Add a new line to the display
@@ -364,12 +378,13 @@ def to_xml(self) -> str:
364378
templ = airspeed.Template(f.read())
365379
return templ.merge(self.lems_info)
366380

367-
def save_to_file(self, file_name: str = None):
381+
def save_to_file(self, file_name: typing.Optional[str] = None):
368382
"""Save LEMSSimulation to a file.
369383
370384
:param file_name: name of file to store to.
371385
`LEMS_<some id string>.xml` is the suggested format. Leave empty
372-
to use `LEMS_<sim_id>.xml` :type file_name: str
386+
to use `LEMS_<sim_id>.xml`
387+
:type file_name: str
373388
:returns: name of file
374389
:rtype: str
375390
"""

pyneuroml/lems/LEMS_TEMPLATE.xml

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,51 @@
11
<Lems>
2-
2+
33
#if ($comment)##
44
<!-- ${comment} -->
5-
6-
#end##
5+
6+
#end##
77
<!-- Specify which component to run -->
88
<Target component="${sim_id}"${report}/>
99

1010
<!-- Include core NeuroML2 ComponentType definitions -->
1111
<Include file="Cells.xml"/>
1212
<Include file="Networks.xml"/>
1313
<Include file="Simulation.xml"/>
14-
14+
1515
#foreach ($include_file in $include_files)##
1616
<Include file="${include_file}"/>
17-
#end##
18-
17+
#end##
18+
1919
#set( $start = -0.1 * $duration )
2020
#set( $end = 1.1 * $duration )
2121
<Simulation id="${sim_id}" length="${duration}ms" step="${dt}ms" target="${target}" seed="${seed}"> <!-- Note seed: ensures same random numbers used every run -->
22-
22+
#if ($meta)##
23+
<Meta for="${meta.for}" method="${meta.method}" abs_tolerance="${meta.abs_tolerance}" rel_tolerance="${meta.rel_tolerance}"/>
24+
#end##
2325
#foreach ($display in $displays)##
2426
<Display id="${display.id}" title="${display.title}" timeScale="${display.time_scale}" xmin="$start" xmax="$end" ymin="${display.ymin}" ymax="${display.ymax}">
2527
#foreach ($line in $display.lines)##
2628
<Line id="${line.id}" quantity="${line.quantity}" scale="${line.scale}" color="${line.color}" timeScale="${line.time_scale}"/>
27-
#end##
29+
#end##
2830
</Display>
29-
30-
#end##
31+
32+
#end##
3133
#foreach ($output_file in $output_files)##
3234
<OutputFile id="${output_file.id}" fileName="${output_file.file_name}">
3335
#foreach ($column in $output_file.columns)##
34-
<OutputColumn id="${column.id}" quantity="${column.quantity}"/>
35-
#end##
36+
<OutputColumn id="${column.id}" quantity="${column.quantity}"/>
37+
#end##
3638
</OutputFile>
37-
38-
#end##
39+
40+
#end##
3941
#foreach ($event_output_file in $event_output_files)##
4042
<EventOutputFile id="${event_output_file.id}" fileName="${event_output_file.file_name}" format="${event_output_file.format}">
4143
#foreach ($selection in $event_output_file.selections)##
42-
<EventSelection id="${selection.id}" select="${selection.select}" eventPort="${selection.event_port}"/>
43-
#end##
44+
<EventSelection id="${selection.id}" select="${selection.select}" eventPort="${selection.event_port}"/>
45+
#end##
4446
</EventOutputFile>
45-
46-
#end##
47+
48+
#end##
4749
</Simulation>
4850

4951
</Lems>

tests/lems/test_lemssimulation.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test the LEMSSimulation class
4+
5+
File: tests/lems/test_lemssimulation.py
6+
7+
Copyright 2023 NeuroML contributors
8+
Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
9+
"""
10+
11+
import logging
12+
import pathlib as pl
13+
import unittest
14+
15+
import pytest
16+
from pyneuroml.lems import LEMSSimulation
17+
18+
logger = logging.getLogger(__name__)
19+
logger.setLevel(logging.DEBUG)
20+
21+
22+
class TestLEMSSimulation(unittest.TestCase):
23+
24+
"""Test the LEMSSimulation class"""
25+
26+
def test_lemssimulation_meta(self):
27+
"""Test the LEMSSimulation class."""
28+
# Create a simulation instance of the model
29+
simulation_id = "tests-sim"
30+
simulation = LEMSSimulation(
31+
sim_id=simulation_id,
32+
duration=1000,
33+
dt=0.1,
34+
simulation_seed=123,
35+
meta={
36+
"for": "neuron",
37+
"method": "cvode",
38+
"abs_tolerance": "0.0001",
39+
"rel_tolerance": "0.0004",
40+
},
41+
)
42+
simulation.assign_simulation_target("some_network")
43+
# Save the simulation to a file
44+
lems_simulation_file = simulation.save_to_file()
45+
46+
expected_string = '<Meta for="neuron" method="cvode" abs_tolerance="0.0001" rel_tolerance="0.0004"/>'
47+
48+
with open(lems_simulation_file, "r") as f:
49+
self.assertIn(expected_string, f.read())
50+
51+
pl.Path(lems_simulation_file).unlink()
52+
53+
@pytest.mark.xfail
54+
def test_lemssimulation_meta_should_fail(self):
55+
"""Test without meta to ensure it's not always added"""
56+
# Create a simulation instance of the model
57+
simulation_id = "tests-sim-failure"
58+
simulation = LEMSSimulation(
59+
sim_id=simulation_id,
60+
duration=1000,
61+
dt=0.1,
62+
simulation_seed=123,
63+
)
64+
simulation.assign_simulation_target("some_network")
65+
# Save the simulation to a file
66+
lems_simulation_file = simulation.save_to_file()
67+
68+
expected_string = '<Meta for="neuron" method="cvode" abs_tolerance="0.0001" rel_tolerance="0.0004"/>'
69+
70+
with open(lems_simulation_file, "r") as f:
71+
self.assertIn(expected_string, f.read())
72+
73+
pl.Path(lems_simulation_file).unlink()

0 commit comments

Comments
 (0)