Skip to content

Commit 03f9475

Browse files
committed
Merge branch 'development' into feat/hybrid-plots
2 parents 2e8c478 + 5a1abf4 commit 03f9475

File tree

17 files changed

+514
-92
lines changed

17 files changed

+514
-92
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,11 @@ arm64
157157
/examples/plot.py
158158
/tests/utils/test_rotation.net.nml
159159
/tests/utils/test_rotation.net.png
160+
/tests/analysis/test_analysis_if.png
161+
/tests/analysis/test_analysis_iv.png
162+
/tests/analysis/test_analysis_traces.png
163+
/tests/analysis/test_cell_analysis.cell.nml
164+
/tests/plot/test_schematic_plot_2d_test_cell_nml_xy.png
165+
/tests/plot/test_schematic_plot_2d_test_cell_nml_xz.png
166+
/tests/plot/test_schematic_plot_2d_test_cell_nml_yz.png
167+
/tests/analysis/*png

examples/LeakConductance.mod

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ NEURON {
1818
RANGE i__LeakConductance : a copy of the variable for current which makes it easier to access from outside the mod file
1919
RANGE gmax : Will be changed when ion channel mechanism placed on cell!
2020
RANGE conductance : parameter
21-
2221
RANGE g : exposure
23-
2422
RANGE fopen : exposure
2523
RANGE conductanceScale : derived variable
2624
RANGE fopen0 : derived variable
@@ -36,11 +34,13 @@ UNITS {
3634
(mV) = (millivolt)
3735
(mS) = (millisiemens)
3836
(uS) = (microsiemens)
37+
(nF) = (nanofarad)
3938
(molar) = (1/liter)
4039
(kHz) = (kilohertz)
4140
(mM) = (millimolar)
4241
(um) = (micrometer)
4342
(umol) = (micromole)
43+
(pC) = (picocoulomb)
4444
(S) = (siemens)
4545

4646
}
@@ -49,7 +49,7 @@ PARAMETER {
4949

5050
gmax = 0 (S/cm2) : Will be changed when ion channel mechanism placed on cell!
5151

52-
conductance = 1.0E-5 (uS)
52+
conductance = 1.0E-5 (uS) : was: 1.0E-11 (conductance)
5353
}
5454

5555
ASSIGNED {
@@ -62,14 +62,10 @@ ASSIGNED {
6262
i (mA/cm2)
6363
i__LeakConductance (mA/cm2)
6464

65-
66-
conductanceScale : derived variable
67-
68-
fopen0 : derived variable
69-
70-
fopen : derived variable
71-
72-
g (uS) : derived variable
65+
conductanceScale : derived variable
66+
fopen0 : derived variable
67+
fopen : derived variable
68+
g (uS) : derived variable
7369

7470
}
7571

pyneuroml/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
__version__ = importlib_metadata.version("pyNeuroML")
99

1010

11-
JNEUROML_VERSION = "0.12.2"
11+
JNEUROML_VERSION = "0.12.3"
1212

1313
# Define a logger for the package
1414
logging.basicConfig(

pyneuroml/analysis/ChannelDensityPlot.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ def plot_channel_densities(
564564
show_plots_already: bool = True,
565565
morph_plot_type: str = "constant",
566566
morph_min_width: float = 2.0,
567+
target_directory=None
567568
):
568569
"""Plot channel densities on a Cell on morphology plots.
569570
@@ -603,6 +604,8 @@ def plot_channel_densities(
603604
:type colormap_name: str
604605
:returns: None
605606
"""
607+
tgt_dir = target_directory+'/' if target_directory else './'
608+
606609
if channel_density_ids is not None and ion_channels is not None:
607610
raise ValueError(
608611
"Only one of channel_density_ids or ions channels may be provided"
@@ -685,7 +688,7 @@ def plot_channel_densities(
685688
min_width=morph_min_width,
686689
overlay_data=data,
687690
overlay_data_label="(S/m2)",
688-
save_to_file=f"{cell.id}_{cd.id}.cd.png",
691+
save_to_file=f"{tgt_dir}{cell.id}_{cd.id}.cd.png",
689692
datamin=ymin,
690693
plane2d=plane2d,
691694
nogui=not show_plots_already,
@@ -710,7 +713,7 @@ def plot_channel_densities(
710713
title_above_plot=True,
711714
xaxis="Distance from soma (um)",
712715
yaxis="g density (S/m2)",
713-
save_figure_to=f"{cell.id}_{cd.id}_cd_vs_dist.png",
716+
save_figure_to=f"{tgt_dir}{cell.id}_{cd.id}_cd_vs_dist.png",
714717
show_plot_already=show_plots_already,
715718
linestyles=[" "],
716719
linewidths=["0"],
@@ -786,7 +789,7 @@ def plot_channel_densities(
786789
min_width=morph_min_width,
787790
overlay_data=data,
788791
overlay_data_label="(S/m2)",
789-
save_to_file=f"{cell.id}_{ion_channel}.ion.png",
792+
save_to_file=f"{tgt_dir}{cell.id}_{ion_channel}.ion.png",
790793
datamin=ymin,
791794
plane2d=plane2d,
792795
nogui=not show_plots_already,
@@ -811,7 +814,7 @@ def plot_channel_densities(
811814
title_above_plot=True,
812815
xaxis="Distance from soma (um)",
813816
yaxis="g density (S/m2)",
814-
save_figure_to=f"{cell.id}_{ion_channel}_ion_vs_dist.png",
817+
save_figure_to=f"{tgt_dir}{cell.id}_{ion_channel}_ion_vs_dist.png",
815818
show_plot_already=show_plots_already,
816819
linestyles=[" "],
817820
linewidths=["0"],

pyneuroml/archive/__init__.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@
77
"""
88

99

10-
import os
11-
import typing
10+
import argparse
1211
import logging
12+
import os
1313
import pathlib
14-
import argparse
1514
import shutil
15+
import typing
1616
from zipfile import ZipFile
17+
18+
from lems.model.model import Model
1719
from neuroml.loaders import read_neuroml2_file
1820
from pyneuroml.pynml import extract_lems_definition_files
1921
from pyneuroml.utils.cli import build_namespace
20-
from lems.model.model import Model
2122

2223
logger = logging.getLogger(__name__)
2324
logger.setLevel(logging.INFO)
@@ -107,7 +108,7 @@ def get_model_file_list(
107108
rootfile: str,
108109
filelist: typing.List[str],
109110
rootdir: str = ".",
110-
lems_def_dir: str = None,
111+
lems_def_dir: typing.Optional[str] = None,
111112
) -> typing.Optional[str]:
112113
"""Get the list of files to archive.
113114
@@ -188,8 +189,8 @@ def get_model_file_list(
188189

189190

190191
def create_combine_archive(
191-
zipfile_name: str,
192192
rootfile: str,
193+
zipfile_name: typing.Optional[str] = None,
193194
zipfile_extension=".neux",
194195
filelist: typing.List[str] = [],
195196
):

pyneuroml/nsgr/__init__.py

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Module for working with NSG. See https://github.com/OpenSourceBrain/pynsgr
4+
5+
File: pyneuroml/nsgr/__init__.py
6+
7+
Copyright 2023 NeuroML contributors
8+
"""
9+
10+
11+
import logging
12+
import os
13+
import pathlib
14+
import shutil
15+
import time
16+
import typing
17+
from zipfile import ZipFile
18+
19+
from pyneuroml.archive import get_model_file_list
20+
from pyneuroml.pynml import run_lems_with
21+
from pyneuroml.utils import get_files_generated_after, get_pyneuroml_tempdir
22+
from pynsgr.commands.nsgr_submit import nsgr_submit
23+
24+
logger = logging.getLogger(__name__)
25+
logger.setLevel(logging.DEBUG)
26+
27+
28+
def run_on_nsg(
29+
engine: str,
30+
lems_file_name: str,
31+
nsg_sim_config: typing.Dict[typing.Any, typing.Any] = {},
32+
run_dir: typing.Optional[str] = None,
33+
dry_run: bool = False,
34+
*engine_args: typing.Any,
35+
**engine_kwargs: typing.Any,
36+
):
37+
"""Run NeuroML/LEMS simulation on NSG.
38+
39+
Since the versions of tools on NSG may differ from what you have installed
40+
locally, this method first generates the simulation engine specific files
41+
(runner script for NEURON and mod files, for example) for the provided
42+
engine in a new folder, zips them up, writes the config files, and then
43+
submits the job to NSG using pynsgr.
44+
45+
The data generated by the job (if the command is not interrupted) is also
46+
downloaded to the newly created directory.
47+
48+
Please ensure that you have set up an account and have your NSG
49+
configuration file populated as noted in pynsgr.
50+
51+
- https://nsgr.sdsc.edu:8443/restusers/documentation
52+
- https://nsgr.sdsc.edu:8443/restusers/docs/tools
53+
54+
Default for the nsg_sim_config is below, keys provided by the user in
55+
nsg_sim_config overwrite these:
56+
57+
.. code:: python
58+
59+
nsg_sim_config_dict = {
60+
"number_cores_": "1",
61+
"number_nodes_": "1",
62+
"tasks_per_node_": "1",
63+
"runtime_": "0.5",
64+
'toolId': "PY_EXPANSE",
65+
'nrnivmodl_o_': "1"
66+
}
67+
68+
.. versionadded:: 1.0.10
69+
70+
:param engine: name of engine: suffixes of the run_lems_with.. functions
71+
:type engine: str
72+
:param lems_file_name: name of LEMS simulation file
73+
:type lems_file_name: str
74+
:param nsg_sim_config: dict containing params and values that will be
75+
printed to testParam.properties
76+
:type nsg_sim_config: dict
77+
:param run_dir: directory in which model files are copied, backend specific
78+
files are generated, and from which the archive is sent to NSG. Results
79+
from the NSG run will also be downloaded to this directory.
80+
81+
By default, this is the directory that the command is run from (".")
82+
83+
It is good practice to separate directories where simulations are run
84+
from the source of the model/simulations.
85+
:type run_dir: str
86+
:param *engine_args: positional args to be passed to the engine runner
87+
function
88+
:param **engine_kwargs: keyword args to be be passed to the engine runner
89+
function
90+
:param dry_run: do everything but do not submit
91+
:type dry_run: bool
92+
"""
93+
supported_engines = ["jneuroml_neuron", "jneuroml_netpyne"]
94+
if engine not in supported_engines:
95+
print(f"Engine {engine} is not currently supported on NSG")
96+
print(f"Supported engines are: {supported_engines}")
97+
return
98+
99+
logger.debug(f"NSGR: engine is {engine}")
100+
101+
zipfile_name = lems_file_name.replace(".xml", "") + "_NSG.zip"
102+
# default dictionary
103+
nsg_sim_config_dict = {
104+
"number_cores_": "1",
105+
"number_nodes_": "1",
106+
"tasks_per_node_": "1",
107+
"runtime_": "0.5",
108+
"toolId": "PY_EXPANSE",
109+
"nrnivmodl_o_": "1",
110+
}
111+
112+
# update dict based on user values
113+
for key, val in nsg_sim_config.items():
114+
nsg_sim_config_dict[key] = val
115+
116+
if run_dir is None:
117+
run_dir = "."
118+
119+
tdir = get_pyneuroml_tempdir(rootdir=run_dir, prefix="pyneuroml")
120+
os.mkdir(tdir)
121+
122+
logger.debug("Getting list of model files")
123+
model_file_list = [] # type: list
124+
lems_def_dir = None
125+
lems_def_dir = get_model_file_list(
126+
lems_file_name, model_file_list, ".", lems_def_dir
127+
)
128+
129+
for model_file in model_file_list:
130+
logger.debug(f"Copying: {model_file} -> {tdir + '/' + model_file}")
131+
# if model file has directory structures in it, recreate the dirs in
132+
# the temporary directory
133+
if len(model_file.split("/")) > 1:
134+
# throw error if files in parent directories are referred to
135+
if "../" in model_file:
136+
raise ValueError(
137+
"""
138+
Cannot handle parent directories because we
139+
cannot create these directories correctly in
140+
the temporary location. Please re-organize
141+
your code such that all included files are in
142+
sub-directories of this main directory.
143+
"""
144+
)
145+
146+
model_file_path = pathlib.Path(tdir + "/" + model_file)
147+
parent = model_file_path.parent
148+
parent.mkdir(parents=True, exist_ok=True)
149+
shutil.copy(model_file, tdir + "/" + model_file)
150+
151+
if lems_def_dir is not None:
152+
logger.info(f"Removing LEMS definitions directory {lems_def_dir}")
153+
shutil.rmtree(lems_def_dir)
154+
155+
os.chdir(tdir)
156+
logger.info(f"Generating simulator specific files in {tdir}")
157+
start_time = time.time() - 1.0
158+
159+
if engine == "jneuroml_neuron":
160+
run_lems_with(
161+
engine,
162+
lems_file_name=lems_file_name,
163+
compile_mods=False,
164+
only_generate_scripts=True,
165+
*engine_args,
166+
**engine_kwargs,
167+
)
168+
elif engine == "jneuroml_netpyne":
169+
run_lems_with(
170+
engine,
171+
lems_file_name=lems_file_name,
172+
only_generate_scripts=True,
173+
*engine_args,
174+
**engine_kwargs,
175+
)
176+
177+
generated_files = get_files_generated_after(
178+
start_time, ignore_suffixes=["xml", "nml"]
179+
)
180+
logger.debug(f"Generated files are: {generated_files}")
181+
182+
logger.info("Generating zip file")
183+
runner_file = ""
184+
# NSG requires that the top level directory exist
185+
nsg_dir = pathlib.Path(zipfile_name.replace(".zip", ""))
186+
logger.debug(f"Creating directory and moving generated files to it: {nsg_dir}")
187+
188+
# remove it if it exists
189+
if nsg_dir.is_dir():
190+
shutil.rmtree(str(nsg_dir))
191+
nsg_dir.mkdir()
192+
193+
with ZipFile(zipfile_name, "w") as archive:
194+
for f in generated_files:
195+
if engine == "jneuroml_neuron":
196+
if f.endswith("_nrn.py"):
197+
runner_file = f
198+
elif engine == "jneuroml_netpyne":
199+
if f.endswith("_netpyne.py"):
200+
runner_file = f
201+
fpath = pathlib.Path(f)
202+
moved_path = fpath.rename(nsg_dir / fpath)
203+
archive.write(str(moved_path))
204+
205+
logger.debug("Printing testParam.properties")
206+
nsg_sim_config_dict["filename_"] = runner_file
207+
logger.debug(f"NSG sim config is: {nsg_sim_config_dict}")
208+
209+
with open("testParam.properties", "w") as file:
210+
for key, val in nsg_sim_config_dict.items():
211+
print(f"{key}={val}", file=file)
212+
213+
logger.debug("Printing testInput.properties")
214+
with open("testInput.properties", "w") as file:
215+
print(f"infile_=@./{zipfile_name}", file=file)
216+
217+
print(f"{zipfile_name} generated")
218+
# uses argv, where the first argument is the script itself, so we must pass
219+
# something as the 0th index of the list
220+
if not dry_run:
221+
if nsgr_submit(["", ".", "validate"]) == 0:
222+
print("Attempting to submit to NSGR")
223+
return nsgr_submit(["", ".", "run"])
224+
else:
225+
print("Dry run mode enabled. Not submitting to NSG.")
226+
227+
return True

0 commit comments

Comments
 (0)