Skip to content
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
8 changes: 8 additions & 0 deletions docs/source/pyneuroml.utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ pyneuroml.utils.cli module
:undoc-members:
:show-inheritance:

pyneuroml.utils.simdata module
-------------------------------

.. automodule:: pyneuroml.utils.simdata
:members:
:undoc-members:
:show-inheritance:

pyneuroml.utils.units module
----------------------------

Expand Down
6 changes: 3 additions & 3 deletions examples/LeakConductance.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ TITLE Mod file for component: Component(id=LeakConductance type=ionChannelHH)
COMMENT

This NEURON file has been generated by org.neuroml.export (see https://github.com/NeuroML/org.neuroml.export)
org.neuroml.export v1.10.1
org.neuroml.model v1.10.1
jLEMS v0.11.1
org.neuroml.export v1.11.0
org.neuroml.model v1.11.0
jLEMS v0.12.0

ENDCOMMENT

Expand Down
17 changes: 8 additions & 9 deletions pyneuroml/archive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
import typing
from zipfile import ZipFile

from pyneuroml.runners import run_jneuroml
from pyneuroml.sedml import validate_sedml_files
from pyneuroml.utils import get_model_file_list
from pyneuroml.utils.cli import build_namespace
import pyneuroml.runners as pynmlr
import pyneuroml.sedml as pynmls
import pyneuroml.utils as pynmlu
import pyneuroml.utils.cli as pynmluc
from pyneuroml.utils.misc import chdir

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -85,7 +85,7 @@ def main(args=None):

def cli(a: typing.Optional[typing.Any] = None, **kwargs: str):
"""Main cli caller method"""
a = build_namespace(DEFAULTS, a, **kwargs)
a = pynmluc.build_namespace(DEFAULTS, a, **kwargs)

rootfile = a.rootfile
zipfile_extension = ".neux.zip"
Expand All @@ -96,13 +96,13 @@ def cli(a: typing.Optional[typing.Any] = None, **kwargs: str):
a.rootfile.startswith("LEMS") and a.rootfile.endswith(".xml")
) and a.sedml is True:
logger.debug("Generating SED-ML file from LEMS file")
run_jneuroml("", a.rootfile, "-sedml")
pynmlr.run_jneuroml("", a.rootfile, "-sedml")

rootfile = a.rootfile.replace(".xml", ".sedml")
zipfile_extension = ".omex.zip"

# validate the generated file
validate_sedml_files([rootfile])
pynmls.validate_sedml_files([rootfile])

# if explicitly given, use that
if a.zipfile_extension is not None:
Expand Down Expand Up @@ -173,10 +173,9 @@ def create_combine_archive(
with chdir(rootdir):
lems_def_dir = None
if len(filelist) == 0:
lems_def_dir = get_model_file_list(
lems_def_dir = pynmlu.get_model_file_list(
rootfile, filelist, rootdir, lems_def_dir
)

create_combine_archive_manifest(rootfile, filelist + extra_files, rootdir)
filelist.append("manifest.xml")

Expand Down
2 changes: 1 addition & 1 deletion pyneuroml/lems/LEMSSimulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from neuroml import __version__ as libnml_ver

from pyneuroml import __version__ as pynml_ver
from pyneuroml.pynml import read_lems_file, read_neuroml2_file
from pyneuroml.io import read_lems_file, read_neuroml2_file
from pyneuroml.utils.plot import get_next_hex_color
from pyneuroml.utils.units import convert_to_units

Expand Down
184 changes: 1 addition & 183 deletions pyneuroml/lems/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import typing

import neuroml
from lxml import etree

from pyneuroml.io import read_neuroml2_file
from pyneuroml.lems.LEMSSimulation import LEMSSimulation
from pyneuroml.pynml import read_neuroml2_file
from pyneuroml.utils.plot import get_next_hex_color

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -480,184 +479,3 @@ def get_pop_index(quantity):
pop = s[0]
index = int(s[1])
return pop, index


def load_sim_data_from_lems_file(
lems_file_name: str,
base_dir: str = ".",
get_events: bool = True,
get_traces: bool = True,
) -> typing.Optional[typing.Union[typing.Tuple[typing.Dict, typing.Dict], typing.Dict]]:
"""Load simulation outputs using the LEMS simulation file

.. versionadded:: 1.2.2

:param lems_file_name: name of LEMS file that was used to generate the data
:type lems_file_name: str
:param base_dir: directory to run in
:type base_dir: str
:returns: if both `get_events` and `get_traces` are selected, a tuple with
two dictionaries, one for traces, one for events, is returned.

Otherwise one dictionary for whichever was selected.

The events dictionary has the following format:

.. code-block:: python

{
'<value of select attribute>': { 'cell id': [<events>] }
}

The traces dictionary has the following format:

.. code-block:: python

{
't': [<values>],
'col 1': [<values>]
'col 2': [<values>]
}

:raises ValueError: if neither traces nor events are selected for loading
:raises ValueError: if no traces are found
:raises ValueError: if no events are found

"""
if not os.path.isfile(lems_file_name):
real_lems_file = os.path.realpath(os.path.join(base_dir, lems_file_name))
else:
real_lems_file = os.path.realpath(lems_file_name)

if not get_events and not get_traces:
raise ValueError("One of events or traces must be True")

logger.debug(
"Reloading data specified in LEMS file: %s (%s), base_dir: %s, cwd: %s;"
% (lems_file_name, real_lems_file, base_dir, os.getcwd())
)

# Could use pylems to parse all this...
traces = {} # type: dict
events = {} # type: dict

base_lems_file_path = os.path.dirname(os.path.realpath(lems_file_name))
tree = etree.parse(real_lems_file)

sim = tree.getroot().find("Simulation")
ns_prefix = ""

possible_prefixes = ["{http://www.neuroml.org/lems/0.7.2}"]
if sim is None:
for pre in possible_prefixes:
for comp in tree.getroot().findall(pre + "Component"):
if comp.attrib["type"] == "Simulation":
ns_prefix = pre
sim = comp

if get_events:
event_output_files = sim.findall(ns_prefix + "EventOutputFile")
for i, of in enumerate(event_output_files):
name = of.attrib["fileName"]
file_name = os.path.join(base_dir, name)
if not os.path.isfile(file_name): # If not relative to the LEMS file...
file_name = os.path.join(base_lems_file_path, name)

# if not os.path.isfile(file_name): # If not relative to the LEMS file...
# file_name = os.path.join(os.getcwd(),name)
# ... try relative to cwd.
# if not os.path.isfile(file_name): # If not relative to the LEMS file...
# file_name = os.path.join(os.getcwd(),'NeuroML2','results',name)
# ... try relative to cwd in NeuroML2/results subdir.
if not os.path.isfile(file_name): # If not relative to the base dir...
raise OSError(
("Could not find simulation output " "file %s" % file_name)
)
format_ = of.attrib["format"]
logger.info(
"Loading saved events from %s (format: %s)" % (file_name, format_)
)
selections = {}
for col in of.findall(ns_prefix + "EventSelection"):
id_ = int(col.attrib["id"])
select = col.attrib["select"]
events[select] = []
selections[id_] = select

with open(file_name) as f:
for line in f:
values = line.split()
if format_ == "TIME_ID":
t = float(values[0])
id_ = int(values[1])
elif format_ == "ID_TIME":
id_ = int(values[0])
t = float(values[1])
if id_ in selections:
logger.debug(
"Found a event in cell %s (%s) at t = %s"
% (id_, selections[id_], t)
)
events[selections[id_]].append(t)

else:
logger.warning("ID %s not found in selections dictionary" % id_)
continue # skip this event

if get_traces:
output_files = sim.findall(ns_prefix + "OutputFile")

for i, of in enumerate(output_files):
traces["t"] = []
name = of.attrib["fileName"]
file_name = os.path.join(base_dir, name)

if not os.path.isfile(file_name): # If not relative to the LEMS file...
file_name = os.path.join(base_lems_file_path, name)

if not os.path.isfile(file_name): # If not relative to the LEMS file...
file_name = os.path.join(os.getcwd(), name)

# ... try relative to cwd.
if not os.path.isfile(file_name): # If not relative to the LEMS file...
file_name = os.path.join(os.getcwd(), "NeuroML2", "results", name)
# ... try relative to cwd in NeuroML2/results subdir.
if not os.path.isfile(file_name): # If not relative to the LEMS file...
raise OSError(
("Could not find simulation output " "file %s" % file_name)
)

logger.info("Loading traces from %s" % (file_name))
cols = []
cols.append("t")
for col in of.findall(ns_prefix + "OutputColumn"):
quantity = col.attrib["quantity"]
traces[quantity] = []
cols.append(quantity)

# TODO: could be quicker using numpy etc?
with open(file_name) as f:
for line in f:
values = line.split()
for vi in range(len(values)):
traces[cols[vi]].append(float(values[vi]))

if get_events is True and get_traces is True:
if len(events) == 0:
raise ValueError("No events found")
if len(traces) == 0:
raise ValueError("No traces found")
logger.debug("Returning both traces and events")
return traces, events
else:
if get_events is True:
if len(events) == 0:
raise ValueError("No events found")
logger.debug("Returning events")
return events
elif get_traces is True:
if len(traces) == 0:
raise ValueError("No traces found")
logger.debug("Returning traces")
return traces
return None
12 changes: 5 additions & 7 deletions pyneuroml/plot/Plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ def generate_plot(

:type legend_position: str
:param show_plot_already: if plot should be shown when created (default: True)
Otherwise, the plots are not shown and you must call pyplot.show()
explicitly to show them. Should not be used with `close_plot`.
:type show_plot_already: boolean
:param animate: if plot should be animated (default: False)
:type animate: boolean
Expand Down Expand Up @@ -207,7 +209,7 @@ def generate_plot(
f"values to plot ({len(xvalues)}) and markersizes ({len(markersizes)}) must have the same length"
)

logger.info("Generating plot: %s" % (title))
logger.info("Generating plot" + (f": {title}" if title else ""))

from matplotlib import pyplot as plt
from matplotlib import rcParams
Expand Down Expand Up @@ -380,15 +382,11 @@ def update(frame):
)

pbar.finish(dirty=False)
logger.info("Saved animation to %s" % (save_figure_to))
logger.info(f"Saved animation to {os.path.abspath(save_figure_to)}")
else:
if save_figure_to:
logger.info(
"Saving image to %s of plot: %s"
% (os.path.abspath(save_figure_to), title)
)
plt.savefig(save_figure_to, bbox_inches="tight")
logger.info("Saved image to %s of plot: %s" % (save_figure_to, title))
logger.info(f"Saved image to {os.path.abspath(save_figure_to)}")

if show_plot_already:
if interactive_legend is True and legend_box is not None:
Expand Down
53 changes: 30 additions & 23 deletions pyneuroml/plot/PlotSpikes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import matplotlib.pyplot as plt
import numpy as np

import pyneuroml.lems as pynmll
import pyneuroml.utils.simdata as pynmls
from pyneuroml.plot import generate_plot
from pyneuroml.utils.cli import build_namespace

Expand Down Expand Up @@ -480,7 +480,7 @@ def plot_spikes_from_lems_file(
:type lems_file_name: str
:param base_dir: Directory where the LEMS file resides. Defaults to the current directory.
:type base_dir: str
:param show_plots_already: Whether to show the plots immediately after they are generated. Defaults to True.
:param show_plots_already: Whether to show the plots
:type show_plots_already: bool
:param save_spike_plot_to: Path to save the spike plot to. If `None`, the plot will not be saved. Defaults to `None`.
:type save_spike_plot_to: Optional[str]
Expand All @@ -493,31 +493,38 @@ def plot_spikes_from_lems_file(
:return: None
:rtype: None
"""
event_data = pynmll.load_sim_data_from_lems_file(
all_events: Dict[str, Dict] = pynmls.load_sim_data_from_lems_file(
lems_file_name, get_events=True, get_traces=False
)

spike_data = [] # type: List[Dict]
for select, times in event_data.items():
new_dict = {"name": select}
new_dict["times"] = times
# the plot_spikes function will add an offset for each data entry, so
# we set the ids to 0 here
new_dict["ids"] = [0] * len(times)

spike_data.append(new_dict)

logger.debug("Spike data is:")
logger.debug(spike_data)
if len(all_events) > 1:
show_each_plot_already = False

for filename, event_data in all_events.items():
spike_data = [] # type: List[Dict]
for select, times in event_data.items():
new_dict = {"name": select}
new_dict["times"] = times
# the plot_spikes function will add an offset for each data entry, so
# we set the ids to 0 here
new_dict["ids"] = [0] * len(times)

spike_data.append(new_dict)

logger.debug("Spike data is:")
logger.debug(spike_data)

plot_spikes(
spike_data,
show_plots_already=show_each_plot_already,
save_spike_plot_to=save_spike_plot_to,
rates=rates,
rate_window=rate_window,
rate_bins=rate_bins,
)

plot_spikes(
spike_data,
show_plots_already=show_plots_already,
save_spike_plot_to=save_spike_plot_to,
rates=rates,
rate_window=rate_window,
rate_bins=rate_bins,
)
if show_plots_already is True and show_each_plot_already is False:
plt.show()


def main(args: Optional[argparse.Namespace] = None) -> None:
Expand Down
Loading
Loading