Skip to content

Commit 609a2a9

Browse files
authored
Merge branch 'main' into katta/fix_ngv_no_connection_cell
2 parents 9c45861 + 6e7dded commit 609a2a9

9 files changed

Lines changed: 204 additions & 166 deletions

File tree

neurodamus/commands.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def neurodamus(args=None):
6767
--num-target-ranks=<number> Number of ranks to target for dry-run load balancing
6868
--coreneuron-direct-mode Run CoreNeuron in direct memory mode transfered from Neuron,
6969
without writing model data to disk.
70+
--use-color=[ON, OFF] If OFF, forces no color to be used in logs; [default: ON]
7071
"""
7172
from . import __version__
7273

neurodamus/core/_neurodamus.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ def h(self):
2828
return self._h
2929

3030
@classmethod
31-
def _init(cls, **kwargs):
31+
def _init(cls, log_filename=LOG_FILENAME, log_use_color=True):
3232
if cls._pc is not None:
3333
return
3434
# Neurodamus requires MPI. We still respect NEURON_INIT_MPI though
3535
_Neuron._init(int(os.environ.get("NEURON_INIT_MPI", "1"))) # if needed, sets cls._h
3636

3737
# Init logging
38-
log_name = kwargs.get("log_filename") or LOG_FILENAME
38+
log_filename = log_filename or LOG_FILENAME
3939
if MPI.rank == 0:
40-
open(log_name, "w", encoding="utf-8").close() # Truncate
40+
open(log_filename, "w", encoding="utf-8").close() # Truncate
4141
MPI.barrier() # Sync so that all processes see the file
42-
setup_logging(GlobalConfig.verbosity, log_name, MPI.rank)
43-
log_stage("Initializing Neurodamus... Logfile: " + log_name)
42+
setup_logging(GlobalConfig.verbosity, log_filename, MPI.rank, use_color=log_use_color)
43+
log_stage("Initializing Neurodamus... Logfile: " + log_filename)
4444

4545
# Load mods if not available
4646
cls._load_nrnmechlibs()

neurodamus/node.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Neurodamus
22
# Copyright 2018 - Blue Brain Project, EPFL
3+
from __future__ import annotations
4+
35
import gc
46
import glob
57
import itertools
@@ -331,12 +333,13 @@ class Node:
331333
_default_population = "All"
332334
"""The default population name for e.g. Reports."""
333335

334-
def __init__(self, config_file, options=None):
336+
def __init__(self, config_file, options: dict | None = None):
335337
"""Creates a neurodamus executor
336338
Args:
337339
config_file: A Sonata config file
338340
options: A dictionary of run options typically coming from cmd line
339341
"""
342+
options = options or {}
340343
assert isinstance(config_file, str), "`config_file` should be a string"
341344
assert config_file, "`config_file` cannot be empty"
342345

@@ -347,7 +350,7 @@ def __init__(self, config_file, options=None):
347350
import libsonata
348351

349352
conf = libsonata.SimulationConfig.from_file(config_file)
350-
Nd.init(log_filename=conf.output.log_file)
353+
Nd.init(log_filename=conf.output.log_file, log_use_color=options.pop("use_color", True))
351354

352355
# This is global initialization, happening once, regardless of number of
353356
# cycles

neurodamus/utils/logging.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def _format_msg(self, record, style):
8888
)
8989

9090

91-
def setup_logging(loglevel, logfile=None, rank=None):
91+
def setup_logging(loglevel, logfile=None, rank=None, use_color=True):
9292
"""Setup neurodamus logging.
9393
Features tabs and colors output to stdout and pydamus.log
9494
@@ -109,17 +109,17 @@ def setup_logging(loglevel, logfile=None, rank=None):
109109
_logging.DEBUG,
110110
]
111111

112-
# Stdout
112+
# stdout
113113
hdlr = _logging.StreamHandler(sys.stdout)
114-
use_color = True
115-
if os.environ.get("ENVIRONMENT") == "BATCH":
116-
use_color = False
117-
else:
118-
try:
119-
sys.stdout.tell() # works only if it's file
114+
if use_color:
115+
if os.environ.get("ENVIRONMENT") == "BATCH":
120116
use_color = False
121-
except OSError:
122-
pass
117+
else:
118+
try:
119+
sys.stdout.tell() # works only if it's file
120+
use_color = False
121+
except OSError:
122+
pass
123123
hdlr.setFormatter(_LevelColorFormatter(with_time=False, rank=rank, use_color=use_color))
124124
if rank == 0:
125125
_logging.root.setLevel(verbosity_levels[loglevel])

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ combine-as-imports = true
153153
"PLR2004", # magic value (constant) used in comparison (i.e. expected == 3)
154154
"S101", # Use of `assert` detected
155155
"SLF001", # private member access
156+
"S404", # `subprocess` module is possibly insecure
157+
"S603", # `subprocess` call: check for execution of untrusted input
158+
"S607", # Starting a process with a partial executable path
156159
]
157160

158161
[tool.ruff.lint.pydocstyle]

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def change_test_dir(monkeypatch, tmp_path):
100100
monkeypatch.chdir(tmp_path)
101101

102102

103-
@pytest.fixture()
103+
@pytest.fixture
104104
def copy_memory_files(change_test_dir):
105105
# Fix values to ensure allocation memory (0,0)[1, 3] (1,0)[2]
106106
metypes_memory = {
Lines changed: 21 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,41 @@
11
import json
2-
import os
32
import subprocess
4-
import tempfile
53
from pathlib import Path
64

5+
import libsonata
6+
77
SIM_DIR = Path(__file__).parent.parent.absolute() / "simulations" / "v5_sonata"
88
CONFIG_FILE_MINI = "simulation_config_mini.json"
99
CIRCUIT_DIR = "sub_mini5"
1010

1111

12-
def test_cli_prcellgid():
13-
from neurodamus import Neurodamus
14-
test_folder = tempfile.TemporaryDirectory("cli-test-prcellgid") # auto removed
15-
test_folder_path = Path(test_folder.name)
16-
with open(SIM_DIR / CONFIG_FILE_MINI, "r") as f:
17-
sim_config_data = json.load(f)
18-
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
19-
with open(test_folder_path / CONFIG_FILE_MINI, "w") as f:
20-
json.dump(sim_config_data, f, indent=2)
21-
22-
os.chdir(test_folder_path)
23-
nd = Neurodamus(CONFIG_FILE_MINI, dump_cell_state=1, keep_build=True
24-
)
25-
nd.run()
26-
assert (test_folder_path / "output_sonata2" / "2_py_Neuron_t0.0.nrndat").is_file()
27-
assert (test_folder_path / "output_sonata2" / "2_py_Neuron_t100.0.nrndat").is_file()
28-
12+
def test_cli_disable_reports(tmp_path):
13+
with open(SIM_DIR / CONFIG_FILE_MINI, encoding="utf-8") as fd:
14+
sim_config_data = json.load(fd)
2915

30-
def test_cli_disable_reports():
31-
test_folder = tempfile.TemporaryDirectory("cli-test-disable-reports") # auto removed
32-
test_folder_path = Path(test_folder.name)
33-
with open(SIM_DIR / CONFIG_FILE_MINI, "r") as f:
34-
sim_config_data = json.load(f)
35-
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
36-
with open(test_folder_path / CONFIG_FILE_MINI, "w") as f:
37-
json.dump(sim_config_data, f, indent=2)
16+
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
17+
with open(tmp_path / CONFIG_FILE_MINI, "w", encoding="utf-8") as fd:
18+
json.dump(sim_config_data, fd)
3819

3920
subprocess.run(
4021
["neurodamus", CONFIG_FILE_MINI, "--disable-reports"],
4122
check=True,
4223
capture_output=True,
43-
cwd=test_folder_path
44-
)
45-
# Spikes are present even if we disable reports
46-
assert (test_folder_path / sim_config_data["output"]["output_dir"] / "out.h5").is_file()
47-
for report in sim_config_data["reports"].keys():
48-
report_path = test_folder_path / sim_config_data["output"]["output_dir"] / (report + ".h5")
49-
assert not report_path.is_file(), f"File '{report_path}' should NOT exist."
50-
51-
subprocess.run(
52-
["neurodamus", CONFIG_FILE_MINI],
53-
check=True,
54-
capture_output=True,
55-
cwd=test_folder_path
24+
cwd=tmp_path
5625
)
57-
assert (test_folder_path / sim_config_data["output"]["output_dir"] / "out.h5").is_file()
58-
for report in sim_config_data["reports"].keys():
59-
report_path = test_folder_path / sim_config_data["output"]["output_dir"] / (report + ".h5")
60-
assert report_path.is_file(), f"File '{report_path}' not found."
61-
62-
63-
def test_cli_keep_build():
64-
from neurodamus import Neurodamus
65-
with open(SIM_DIR / CONFIG_FILE_MINI, "r") as f:
66-
sim_config_data = json.load(f)
67-
sim_config_data["target_simulator"] = "CORENEURON"
68-
sim_config_data["output"]["output_dir"] = "output_keep_build"
69-
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
70-
71-
test_folder = tempfile.TemporaryDirectory("cli-test-keep-build") # auto removed
72-
test_folder_path = Path(test_folder.name)
73-
with open(test_folder_path / CONFIG_FILE_MINI, "w") as f:
74-
json.dump(sim_config_data, f, indent=2)
75-
76-
os.chdir(test_folder_path)
77-
nd = Neurodamus(CONFIG_FILE_MINI, keep_build=True, disable_reports=True)
78-
nd.run()
79-
coreneuron_input_dir = test_folder_path / "build" / "coreneuron_input"
80-
assert coreneuron_input_dir.is_dir(), "Directory 'coreneuron_input' not found."
8126

27+
sc = libsonata.SimulationConfig.from_file(CONFIG_FILE_MINI)
28+
spikes_path = tmp_path / sc.output.output_dir / "out.h5"
8229

83-
def test_cli_build_model():
84-
with open(SIM_DIR / CONFIG_FILE_MINI, "r") as f:
85-
sim_config_data = json.load(f)
86-
sim_config_data["target_simulator"] = "CORENEURON"
87-
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
88-
89-
test_folder = tempfile.TemporaryDirectory("cli-test-build-model") # auto removed
90-
test_folder_path = Path(test_folder.name)
91-
with open(test_folder_path / CONFIG_FILE_MINI, "w") as f:
92-
json.dump(sim_config_data, f, indent=2)
93-
94-
result_model = subprocess.run(
95-
["neurodamus", CONFIG_FILE_MINI, "--simulate-model=OFF", "--disable-reports"],
96-
check=True,
97-
cwd=test_folder_path,
98-
capture_output=True,
99-
text=True
100-
)
101-
assert "[SKIPPED] SIMULATION (MODEL BUILD ONLY)" in result_model.stdout
102-
103-
result_auto = subprocess.run(
104-
["neurodamus", CONFIG_FILE_MINI, "--disable-reports"],
105-
check=True,
106-
cwd=test_folder_path,
107-
capture_output=True,
108-
text=True
109-
)
110-
assert "SIMULATION (SKIP MODEL BUILD)" in result_auto.stdout
111-
112-
subprocess.run(
113-
["neurodamus", CONFIG_FILE_MINI, "--simulate-model=OFF", "--disable-reports"],
114-
check=True,
115-
cwd=test_folder_path
116-
)
117-
result_off = subprocess.run(
118-
["neurodamus", CONFIG_FILE_MINI, "--build-model=OFF", "--disable-reports"],
119-
check=True,
120-
cwd=test_folder_path,
121-
capture_output=True,
122-
text=True
123-
)
124-
assert "SIMULATION (SKIP MODEL BUILD)" in result_off.stdout
125-
126-
127-
def test_cli_lb_mode():
128-
test_folder = tempfile.TemporaryDirectory("cli-test-lb-mode") # auto removed
129-
test_folder_path = Path(test_folder.name)
130-
with open(SIM_DIR / CONFIG_FILE_MINI, "r") as f:
131-
sim_config_data = json.load(f)
132-
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
133-
with open(test_folder_path / CONFIG_FILE_MINI, "w") as f:
134-
json.dump(sim_config_data, f, indent=2)
135-
136-
for lb_mode in ("WholeCell", "MultiSplit"):
137-
result = subprocess.run(
138-
["neurodamus", CONFIG_FILE_MINI, f"--lb-mode={lb_mode}", "--disable-reports"],
139-
check=True,
140-
cwd=test_folder_path,
141-
capture_output=True,
142-
text=True
143-
)
144-
assert f"Load Balancing ENABLED. Mode: {lb_mode}" in result.stdout
145-
assert (test_folder_path / "mcomplex.dat").is_file(), "File 'mcomplex.dat' not found."
146-
assert (test_folder_path / "sim_conf").is_dir(), "Directory 'sim_conf' not found."
147-
30+
# Spikes are present even if we disable reports
31+
assert spikes_path.is_file()
14832

149-
def test_cli_output_path():
150-
from neurodamus import Neurodamus
151-
test_folder = tempfile.TemporaryDirectory("cli-test-output-path") # auto removed
152-
test_folder_path = Path(test_folder.name)
153-
with open(SIM_DIR / CONFIG_FILE_MINI, "r") as f:
154-
sim_config_data = json.load(f)
155-
sim_config_data["network"] = str(SIM_DIR / CIRCUIT_DIR / "circuit_config.json")
156-
with open(test_folder_path / CONFIG_FILE_MINI, "w") as f:
157-
json.dump(sim_config_data, f, indent=2)
33+
for name in sc.list_report_names:
34+
report_path = Path(sc.report(name).file_name)
35+
assert not report_path.is_file(), f"File '{report_path}' should NOT exist."
15836

159-
simconfig_output_path = sim_config_data["output"]["output_dir"]
160-
output_path = "new_output"
161-
os.chdir(test_folder_path)
162-
nd = Neurodamus(CONFIG_FILE_MINI, output_path=output_path)
163-
nd.run()
164-
# Output directory from simulation configuration is overridden
165-
assert not (test_folder_path / simconfig_output_path).is_dir(), \
166-
f"Directory '{simconfig_output_path}' should NOT exist."
167-
assert (test_folder_path / output_path).is_dir(), f"Directory '{output_path}' not found."
37+
subprocess.run(["neurodamus", CONFIG_FILE_MINI], cwd=tmp_path, check=True)
38+
assert spikes_path.is_file()
39+
for name in sc.list_report_names:
40+
report_path = Path(sc.report(name).file_name)
41+
assert report_path.is_file(), f"File '{report_path}' not found."

0 commit comments

Comments
 (0)