Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor LEMSSimulation to remove the need of an explicit dict (just use vars(self) instead #427

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
107 changes: 46 additions & 61 deletions pyneuroml/lems/LEMSSimulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import os
import os.path
import random
import typing
from typing import Optional
from typing import Any, Dict, List, Optional, Union

import airspeed
from neuroml import __version__ as libnml_ver
Expand All @@ -28,20 +27,19 @@ class LEMSSimulation:

TEMPLATE_FILE = "%s/LEMS_TEMPLATE.xml" % (os.path.dirname(__file__))

lems_info: typing.Dict[str, typing.Any] = {}
my_random = random.Random()

def __init__(
self,
sim_id: str,
duration: typing.Union[float, str],
dt: typing.Union[float, str],
target: typing.Optional[str] = None,
duration: Union[float, str],
dt: Union[float, str],
target: Optional[str] = None,
comment: str = "\n\n This LEMS file has been automatically generated using PyNeuroML v%s (libNeuroML v%s)\n\n "
% (pynml_ver, libnml_ver),
lems_file_generate_seed: Optional[typing.Any] = None,
lems_file_generate_seed: Optional[Any] = None,
simulation_seed: int = 12345,
meta: typing.Optional[typing.Dict[str, str]] = None,
meta: Optional[Dict[str, str]] = None,
) -> None:
"""Init a new LEMSSimulation object.

Expand Down Expand Up @@ -105,21 +103,21 @@ def __init__(
else:
dt_mag_ms = float(dt)

self.lems_info["sim_id"] = sim_id
self.lems_info["duration"] = duration_mag_ms
self.lems_info["dt"] = dt_mag_ms
self.lems_info["comment"] = comment
self.lems_info["seed"] = int(simulation_seed)
self.lems_info["report"] = ""
self.sim_id = sim_id
self.duration = duration_mag_ms
self.dt = dt_mag_ms
self.comment = comment
self.seed = int(simulation_seed)
self.report = ""

self.lems_info["include_files"] = []
self.lems_info["displays"] = []
self.lems_info["output_files"] = []
self.lems_info["event_output_files"] = []
self.lems_info["meta"] = meta
self.include_files: List[str] = []
self.displays: List[Dict[str, Any]] = []
self.output_files: List[Dict[str, Any]] = []
self.event_output_files: List[Dict[str, Any]] = []
self.meta = meta

if target:
self.lems_info["target"] = target
self.target = target

if lems_file_generate_seed:
self.my_random.seed(
Expand All @@ -128,22 +126,12 @@ def __init__(
else:
self.my_random.seed(12345)

self.lems_file_name = f"LEMS_{self.lems_info['sim_id']}.xml"
self.lems_file_name = f"LEMS_{self.sim_id}.xml"

def __setattr__(self, attr: typing.Any, value: typing.Any) -> None:
"""Set an attribute value.

:param attr: attribute to set value of
:type attr: Any
:param value: value to set
:type value: Any
:returns: None
:raises AttributeError: if provided attribute is not found in LEMSSimulation.
"""
if attr in self.lems_info.keys():
self.lems_info[attr] = value
else:
raise AttributeError("There is not a field: %s in LEMSSimulation" % attr)
@property
def lems_info(self):
"""Return the attributes"""
return vars(self)

def assign_simulation_target(self, target: str) -> None:
"""Assign a simulation target.
Expand All @@ -152,7 +140,7 @@ def assign_simulation_target(self, target: str) -> None:
:type target: str
:returns: None
"""
self.lems_info["target"] = target
self.target = target

def set_report_file(self, report_file_name: str) -> None:
"""Set a report file.
Expand All @@ -164,7 +152,7 @@ def set_report_file(self, report_file_name: str) -> None:
:returns: None
"""
if report_file_name is not None:
self.lems_info["report"] = ' reportFile="%s"' % report_file_name
self.report = ' reportFile="%s"' % report_file_name

def include_neuroml2_file(
self,
Expand All @@ -190,8 +178,8 @@ def include_neuroml2_file(
full_path = os.path.abspath(relative_to_dir + "/" + nml2_file_name)
base_path = os.path.dirname(full_path)
# logger.info_v("Including in generated LEMS file: %s (%s)"%(nml2_file_name, full_path))
if nml2_file_name not in self.lems_info["include_files"]:
self.lems_info["include_files"].append(nml2_file_name)
if nml2_file_name not in self.include_files:
self.include_files.append(nml2_file_name)

if include_included:
cell = read_neuroml2_file(full_path)
Expand All @@ -212,13 +200,13 @@ def include_lems_file(
:type include_included: bool
:returns: None
"""
if lems_file_name not in self.lems_info["include_files"]:
self.lems_info["include_files"].append(lems_file_name)
if lems_file_name not in self.include_files:
self.include_files.append(lems_file_name)

if include_included:
model = read_lems_file(lems_file_name)
for inc in model.included_files:
self.lems_info["include_files"].append(inc)
self.include_files.append(inc)

def create_display(
self, id: str, title: str, ymin: str, ymax: str, timeScale: str = "1ms"
Expand All @@ -237,8 +225,8 @@ def create_display(
:type timeScale: str
:returns: None
"""
disp = {} # type: dict[str, typing.Any]
self.lems_info["displays"].append(disp)
disp = {} # type: dict[str, Any]
self.displays.append(disp)
disp["id"] = id
disp["title"] = title
disp["ymin"] = ymin
Expand All @@ -259,8 +247,8 @@ def create_output_file(self, id: str, file_name: str):
:type file_name: str
:returns: None
"""
of = {} # type: dict[str, typing.Any]
self.lems_info["output_files"].append(of)
of = {} # type: dict[str, Any]
self.output_files.append(of)
of["id"] = id
of["file_name"] = file_name
of["columns"] = []
Expand All @@ -283,7 +271,7 @@ def create_event_output_file(self, id, file_name, format="ID_TIME"):
:returns: None
"""
eof = {}
self.lems_info["event_output_files"].append(eof)
self.event_output_files.append(eof)
eof["id"] = id
eof["file_name"] = file_name
eof["format"] = format
Expand All @@ -295,7 +283,7 @@ def add_line_to_display(
line_id: str,
quantity: str,
scale: str = "1",
color: typing.Optional[str] = None,
color: Optional[str] = None,
timeScale: str = "1ms",
) -> None:
"""Add a new line to the display
Expand All @@ -316,15 +304,15 @@ def add_line_to_display(
:raises ValueError: if provided `display_id` has not been created yet.
"""
disp = None
for d in self.lems_info["displays"]:
for d in self.displays:
if d["id"] == display_id:
disp = d
if not disp:
raise ValueError(
f"Display with id {display_id} not found. Please check the provided display_id, or create it first."
)

line = {} # type: dict[str, typing.Any]
line = {} # type: dict[str, Any]
disp["lines"].append(line)
line["id"] = line_id
line["quantity"] = quantity
Expand All @@ -348,7 +336,7 @@ def add_column_to_output_file(
:raises ValueError: if provided `output_file_id` has not been created yet.
"""
of = None
for o in self.lems_info["output_files"]:
for o in self.output_files:
if o["id"] == output_file_id:
of = o

Expand All @@ -357,7 +345,7 @@ def add_column_to_output_file(
f"Output file with id {output_file_id} not found. Please check the provided output_file_id, or create it first."
)

column = {} # type: dict[str, typing.Any]
column = {} # type: dict[str, Any]
of["columns"].append(column)
column["id"] = column_id
column["quantity"] = quantity
Expand All @@ -381,15 +369,15 @@ def add_selection_to_event_output_file(
:raises ValueError: if provided `event_output_file_id` has not been created yet.
"""
eof = None
for o in self.lems_info["event_output_files"]:
for o in self.event_output_files:
if o["id"] == event_output_file_id:
eof = o
if not eof:
raise ValueError(
f"Output file with id {event_output_file_id} not found. Please check the provided event_output_file_id, or create it first."
)

selection = {} # type: dict[str, typing.Any]
selection = {} # type: dict[str, Any]
eof["selections"].append(selection)
selection["id"] = event_id
selection["select"] = select
Expand All @@ -406,9 +394,10 @@ def to_xml(self) -> str:
templfile = "." + templfile
with open(templfile) as f:
templ = airspeed.Template(f.read())

return templ.merge(self.lems_info)

def save_to_file(self, file_name: typing.Optional[str] = None):
def save_to_file(self, file_name: Optional[str] = None):
"""Save LEMSSimulation to a file.

:param file_name: name of file to store to.
Expand All @@ -427,18 +416,14 @@ def save_to_file(self, file_name: typing.Optional[str] = None):
self.lems_file_name = file_name

logger.info(
"Writing LEMS Simulation %s to file: %s..."
% (self.lems_info["sim_id"], file_name)
"Writing LEMS Simulation %s to file: %s..." % (self.sim_id, file_name)
)
with open(file_name, "w") as lems_file:
lems_file.write(self.to_xml())
lems_file.flush()
os.fsync(lems_file.fileno())

logger.info(
"Written LEMS Simulation %s to file: %s"
% (self.lems_info["sim_id"], file_name)
)
logger.info("Written LEMS Simulation %s to file: %s" % (self.sim_id, file_name))

return file_name

Expand Down
13 changes: 7 additions & 6 deletions pyneuroml/lems/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ def generate_lems_file_for_neuroml(
sim_id: str,
neuroml_file: str,
target: str,
duration: str,
dt: str,
duration: typing.Union[str, float, int],
dt: typing.Union[str, float, int],
lems_file_name: str,
target_dir: str,
nml_doc: typing.Optional[neuroml.NeuroMLDocument] = None,
Expand Down Expand Up @@ -55,9 +55,9 @@ def generate_lems_file_for_neuroml(
:param target: target element
:type target: str
:param duration: simulation duration
:type duration: str
:type duration: str or float or int
:param dt: integration time step
:type dt: str
:type dt: str or float or int
:param lems_file_name: name of LEMS file
:type lems_file_name: str
:param target_dir: directory to place LEMS file in
Expand Down Expand Up @@ -153,7 +153,8 @@ def generate_lems_file_for_neuroml(
else:
nml_doc_inc_not_included = nml_doc

ls.set_report_file(report_file_name)
if report_file_name is not None:
ls.set_report_file(report_file_name)

quantities_saved = []

Expand Down Expand Up @@ -395,7 +396,7 @@ def generate_lems_file_for_neuroml(
for i in range(size):
quantity = quantity_template_e % (population.id, i)
ls.add_selection_to_event_output_file(
eof0, i, quantity, "spike"
eof0, str(i), quantity, "spike"
)
quantities_saved.append(quantity)

Expand Down
Loading