bluecellulab 2.6.51__tar.gz → 2.6.52__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bluecellulab might be problematic. Click here for more details.
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/PKG-INFO +1 -1
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/core.py +16 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/sonata_simulation_config.py +44 -1
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/iotools.py +1 -1
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit_simulation.py +106 -5
- bluecellulab-2.6.52/bluecellulab/simulation/report.py +227 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/tools.py +78 -12
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab.egg-info/PKG-INFO +1 -1
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab.egg-info/SOURCES.txt +1 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.compile_mod.sh +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.gitattributes +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.github/dependabot.yml +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.github/workflows/release.yml +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.github/workflows/test.yml +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.gitignore +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.gitlab-ci.yml +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.readthedocs.yml +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/.zenodo.json +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/AUTHORS.txt +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/CHANGELOG.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/CITATION.cff +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/CONTRIBUTING.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/LICENSE +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/MANIFEST.in +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/Makefile +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/README.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/analysis/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/analysis/analysis.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/analysis/inject_sequence.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/analysis/plotting.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/analysis/utils.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/ballstick/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/ballstick/emodel.hoc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/ballstick/morphology.asc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/cell_dict.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/injector.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/plotting.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/random.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/recording.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/section_distance.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/serialized_sections.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/sonata_proxy.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/stimuli_generator.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/cell/template.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/circuit_access/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/circuit_access/bluepy_circuit_access.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/circuit_access/definition.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/circuit_access/sonata_circuit_access.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/bluepy_simulation_config.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/definition.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/sections.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/format.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/node_id.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/simulation_access.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/synapse_properties.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/validate.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/connection.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/dendrogram.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/exceptions.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/graph.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/hoc/Cell.hoc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/hoc/RNGSettings.hoc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/hoc/TDistFunc.hoc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/hoc/TStim.hoc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/hoc/fileUtils.hoc +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/importer.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/neuron_interpreter.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/plotwindow.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/psection.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/psegment.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/rngsettings.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/simulation/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/simulation/neuron_globals.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/simulation/parallel.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/simulation/simulation.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/stimulus/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/stimulus/circuit_stimulus_definitions.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/stimulus/factory.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/stimulus/stimulus.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/synapse/__init__.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/synapse/synapse_factory.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/synapse/synapse_types.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/type_aliases.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/utils.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/validation/validation.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/verbosity.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab.egg-info/dependency_links.txt +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab.egg-info/requires.txt +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab.egg-info/top_level.txt +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/Makefile +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/images/voltage-readme.png +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/make.bat +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/requirements_docs.txt +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/_static/.gitkeep +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/api.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/changelog.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/compiling-mechanisms.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/conf.py +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/contributing.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/index.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/list_of_stim.rst +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/docs/source/logo/BlueCelluLabBanner.jpg +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/pyproject.toml +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/setup.cfg +0 -0
- {bluecellulab-2.6.51 → bluecellulab-2.6.52}/tox.ini +0 -0
|
@@ -525,6 +525,10 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
525
525
|
nc.record(spike_vec)
|
|
526
526
|
self.recordings[f"spike_detector_{location}_{threshold}"] = spike_vec
|
|
527
527
|
|
|
528
|
+
def is_recording_spikes(self, location: str, threshold: float) -> bool:
|
|
529
|
+
key = f"spike_detector_{location}_{threshold}"
|
|
530
|
+
return key in self.recordings
|
|
531
|
+
|
|
528
532
|
def get_recorded_spikes(self, location: str, threshold: float = -30) -> list[float]:
|
|
529
533
|
"""Get recorded spikes in the current cell.
|
|
530
534
|
|
|
@@ -756,6 +760,18 @@ class Cell(InjectableMixin, PlottableMixin):
|
|
|
756
760
|
"""Get a vector of AIS voltage."""
|
|
757
761
|
return self.get_recording('self.axonal[1](0.5)._ref_v')
|
|
758
762
|
|
|
763
|
+
def add_variable_recording(self, variable: str, section, segx):
|
|
764
|
+
if variable == "v":
|
|
765
|
+
self.add_voltage_recording(section, segx)
|
|
766
|
+
else:
|
|
767
|
+
raise ValueError(f"Unsupported variable for recording: {variable}")
|
|
768
|
+
|
|
769
|
+
def get_variable_recording(self, variable: str, section, segx) -> np.ndarray:
|
|
770
|
+
if variable == "v":
|
|
771
|
+
return self.get_voltage_recording(section=section, segx=segx)
|
|
772
|
+
else:
|
|
773
|
+
raise ValueError(f"Unsupported variable '{variable}'")
|
|
774
|
+
|
|
759
775
|
@property
|
|
760
776
|
def n_segments(self) -> int:
|
|
761
777
|
"""Get the number of segments in the cell."""
|
{bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/sonata_simulation_config.py
RENAMED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
from functools import lru_cache
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
16
18
|
from pathlib import Path
|
|
17
19
|
from typing import Optional
|
|
18
20
|
|
|
@@ -21,6 +23,8 @@ from bluecellulab.stimulus.circuit_stimulus_definitions import Stimulus
|
|
|
21
23
|
|
|
22
24
|
from bluepysnap import Simulation as SnapSimulation
|
|
23
25
|
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
24
28
|
|
|
25
29
|
class SonataSimulationConfig:
|
|
26
30
|
"""Sonata implementation of SimulationConfig protocol."""
|
|
@@ -74,9 +78,42 @@ class SonataSimulationConfig:
|
|
|
74
78
|
result.append(ConnectionOverrides.from_sonata(conn_entry))
|
|
75
79
|
return result
|
|
76
80
|
|
|
81
|
+
@lru_cache(maxsize=1)
|
|
82
|
+
def get_compartment_sets(self) -> dict[str, dict]:
|
|
83
|
+
filepath = self.impl.config.get("compartment_sets_file")
|
|
84
|
+
if not filepath:
|
|
85
|
+
raise ValueError("No 'compartment_sets_file' entry found in SONATA config.")
|
|
86
|
+
with open(filepath, 'r') as f:
|
|
87
|
+
return json.load(f)
|
|
88
|
+
|
|
89
|
+
@lru_cache(maxsize=1)
|
|
90
|
+
def get_node_sets(self) -> dict[str, dict]:
|
|
91
|
+
filepath = self.impl.circuit.config.get("node_sets_file")
|
|
92
|
+
if not filepath:
|
|
93
|
+
raise ValueError("No 'node_sets_file' entry found in SONATA config.")
|
|
94
|
+
with open(filepath, 'r') as f:
|
|
95
|
+
return json.load(f)
|
|
96
|
+
|
|
97
|
+
@lru_cache(maxsize=1)
|
|
98
|
+
def get_report_entries(self) -> dict[str, dict]:
|
|
99
|
+
"""Returns the 'reports' dictionary from the SONATA simulation config.
|
|
100
|
+
|
|
101
|
+
Each key is a report name, and the value is its configuration.
|
|
102
|
+
"""
|
|
103
|
+
reports = self.impl.config.get("reports", {})
|
|
104
|
+
if not isinstance(reports, dict):
|
|
105
|
+
raise ValueError("Invalid format for 'reports' in SONATA config.")
|
|
106
|
+
return reports
|
|
107
|
+
|
|
77
108
|
def connection_entries(self) -> list[ConnectionOverrides]:
|
|
78
109
|
return self._connection_entries() + self._connection_overrides
|
|
79
110
|
|
|
111
|
+
def report_file_path(self, report_cfg: dict, report_key: str) -> Path:
|
|
112
|
+
"""Resolve the full path for the report output file."""
|
|
113
|
+
output_dir = Path(self.output_root_path)
|
|
114
|
+
file_name = report_cfg.get("file_name", f"{report_key}.h5")
|
|
115
|
+
return output_dir / file_name
|
|
116
|
+
|
|
80
117
|
@property
|
|
81
118
|
def base_seed(self) -> int:
|
|
82
119
|
return self.impl.run.random_seed
|
|
@@ -135,7 +172,13 @@ class SonataSimulationConfig:
|
|
|
135
172
|
|
|
136
173
|
@property
|
|
137
174
|
def output_root_path(self) -> str:
|
|
138
|
-
return self.impl.config
|
|
175
|
+
return self.impl.config.get("output", {}).get("output_dir", "output")
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def spikes_file_path(self) -> Path:
|
|
179
|
+
output_dir = Path(self.output_root_path)
|
|
180
|
+
spikes_file = self.impl.config.get("output", {}).get("spikes_file", "spikes.h5")
|
|
181
|
+
return output_dir / spikes_file
|
|
139
182
|
|
|
140
183
|
@property
|
|
141
184
|
def extracellular_calcium(self) -> Optional[float]:
|
|
@@ -17,7 +17,6 @@ from __future__ import annotations
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import logging
|
|
19
19
|
|
|
20
|
-
import bluepy
|
|
21
20
|
import numpy as np
|
|
22
21
|
|
|
23
22
|
from bluecellulab.circuit.node_id import CellId
|
|
@@ -28,6 +27,7 @@ logger = logging.getLogger(__name__)
|
|
|
28
27
|
def parse_outdat(path: str | Path) -> dict[CellId, np.ndarray]:
|
|
29
28
|
"""Parse the replay spiketrains in a out.dat formatted file pointed to by
|
|
30
29
|
path."""
|
|
30
|
+
import bluepy
|
|
31
31
|
spikes = bluepy.impl.spike_report.SpikeReport.load(path).get()
|
|
32
32
|
# convert Series to DataFrame with 2 columns for `groupby` operation
|
|
33
33
|
spike_df = spikes.to_frame().reset_index()
|
|
@@ -17,10 +17,12 @@ simulations."""
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
from collections.abc import Iterable
|
|
20
|
+
import os
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
from typing import Optional
|
|
22
23
|
import logging
|
|
23
24
|
|
|
25
|
+
from collections import defaultdict
|
|
24
26
|
import neuron
|
|
25
27
|
import numpy as np
|
|
26
28
|
import pandas as pd
|
|
@@ -45,6 +47,7 @@ from bluecellulab.circuit.simulation_access import BluepySimulationAccess, Simul
|
|
|
45
47
|
from bluecellulab.importer import load_mod_files
|
|
46
48
|
from bluecellulab.rngsettings import RNGSettings
|
|
47
49
|
from bluecellulab.simulation.neuron_globals import NeuronGlobals
|
|
50
|
+
from bluecellulab.simulation.report import configure_all_reports, write_compartment_report, write_sonata_spikes
|
|
48
51
|
from bluecellulab.stimulus.circuit_stimulus_definitions import Noise, OrnsteinUhlenbeck, RelativeOrnsteinUhlenbeck, RelativeShotNoise, ShotNoise
|
|
49
52
|
import bluecellulab.stimulus.circuit_stimulus_definitions as circuit_stimulus_definitions
|
|
50
53
|
from bluecellulab.exceptions import BluecellulabError
|
|
@@ -301,6 +304,16 @@ class CircuitSimulation:
|
|
|
301
304
|
add_linear_stimuli=add_linear_stimuli
|
|
302
305
|
)
|
|
303
306
|
|
|
307
|
+
configure_all_reports(
|
|
308
|
+
cells=self.cells,
|
|
309
|
+
simulation_config=self.circuit_access.config
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# add spike recordings
|
|
313
|
+
for cell in self.cells.values():
|
|
314
|
+
if not cell.is_recording_spikes("soma", threshold=self.spike_threshold):
|
|
315
|
+
cell.start_recording_spikes(None, location="soma", threshold=self.spike_threshold)
|
|
316
|
+
|
|
304
317
|
def _add_stimuli(self, add_noise_stimuli=False,
|
|
305
318
|
add_hyperpolarizing_stimuli=False,
|
|
306
319
|
add_relativelinear_stimuli=False,
|
|
@@ -458,13 +471,26 @@ class CircuitSimulation:
|
|
|
458
471
|
@staticmethod
|
|
459
472
|
def merge_pre_spike_trains(*train_dicts) -> dict[CellId, np.ndarray]:
|
|
460
473
|
"""Merge presynaptic spike train dicts."""
|
|
461
|
-
filtered_dicts = [d for d in train_dicts if d
|
|
474
|
+
filtered_dicts = [d for d in train_dicts if isinstance(d, dict) and d]
|
|
475
|
+
|
|
476
|
+
if not filtered_dicts:
|
|
477
|
+
logger.warning("merge_pre_spike_trains: No presynaptic spike trains found.")
|
|
478
|
+
return {}
|
|
462
479
|
|
|
463
480
|
all_keys = set().union(*[d.keys() for d in filtered_dicts])
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
481
|
+
result = {}
|
|
482
|
+
|
|
483
|
+
for k in all_keys:
|
|
484
|
+
valid_arrays = []
|
|
485
|
+
for d in filtered_dicts:
|
|
486
|
+
if k in d:
|
|
487
|
+
val = d[k]
|
|
488
|
+
if isinstance(val, (np.ndarray, list)) and len(val) > 0:
|
|
489
|
+
valid_arrays.append(np.asarray(val))
|
|
490
|
+
if valid_arrays:
|
|
491
|
+
result[k] = np.sort(np.concatenate(valid_arrays))
|
|
492
|
+
|
|
493
|
+
return result
|
|
468
494
|
|
|
469
495
|
def _add_connections(
|
|
470
496
|
self,
|
|
@@ -646,6 +672,8 @@ class CircuitSimulation:
|
|
|
646
672
|
forward_skip_value=forward_skip_value,
|
|
647
673
|
show_progress=show_progress)
|
|
648
674
|
|
|
675
|
+
self.write_reports()
|
|
676
|
+
|
|
649
677
|
def get_mainsim_voltage_trace(
|
|
650
678
|
self, cell_id: int | tuple[str, int], t_start=None, t_stop=None, t_step=None
|
|
651
679
|
) -> np.ndarray:
|
|
@@ -779,3 +807,76 @@ class CircuitSimulation:
|
|
|
779
807
|
record_dt=cell_kwargs['record_dt'],
|
|
780
808
|
template_format=cell_kwargs['template_format'],
|
|
781
809
|
emodel_properties=cell_kwargs['emodel_properties'])
|
|
810
|
+
|
|
811
|
+
def write_reports(self):
|
|
812
|
+
"""Write all reports defined in the simulation config."""
|
|
813
|
+
report_entries = self.circuit_access.config.get_report_entries()
|
|
814
|
+
|
|
815
|
+
for report_name, report_cfg in report_entries.items():
|
|
816
|
+
report_type = report_cfg.get("type", "compartment")
|
|
817
|
+
section = report_cfg.get("sections")
|
|
818
|
+
|
|
819
|
+
if report_type != "compartment":
|
|
820
|
+
raise NotImplementedError(f"Report type '{report_type}' is not supported.")
|
|
821
|
+
|
|
822
|
+
output_path = self.circuit_access.config.report_file_path(report_cfg, report_name)
|
|
823
|
+
if section == "compartment_set":
|
|
824
|
+
if report_cfg.get("cells") is not None:
|
|
825
|
+
raise ValueError(
|
|
826
|
+
"Report config error: 'cells' must not be set when using 'compartment_set' sections."
|
|
827
|
+
)
|
|
828
|
+
compartment_sets = self.circuit_access.config.get_compartment_sets()
|
|
829
|
+
write_compartment_report(
|
|
830
|
+
report_name=report_name,
|
|
831
|
+
output_path=output_path,
|
|
832
|
+
cells=self.cells,
|
|
833
|
+
report_cfg=report_cfg,
|
|
834
|
+
source_sets=compartment_sets,
|
|
835
|
+
source_type="compartment_set"
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
else:
|
|
839
|
+
node_sets = self.circuit_access.config.get_node_sets()
|
|
840
|
+
if report_cfg.get("compartments") not in ("center", "all"):
|
|
841
|
+
raise ValueError(
|
|
842
|
+
f"Unsupported 'compartments' value '{report_cfg.get('compartments')}' "
|
|
843
|
+
"for node-based section recording (must be 'center' or 'all')."
|
|
844
|
+
)
|
|
845
|
+
write_compartment_report(
|
|
846
|
+
report_name=report_name,
|
|
847
|
+
output_path=output_path,
|
|
848
|
+
cells=self.cells,
|
|
849
|
+
report_cfg=report_cfg,
|
|
850
|
+
source_sets=node_sets,
|
|
851
|
+
source_type="node_set"
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
self.write_spike_report()
|
|
855
|
+
|
|
856
|
+
def write_spike_report(self):
|
|
857
|
+
"""Collect and write in-memory recorded spike times to a SONATA HDF5
|
|
858
|
+
file, grouped by population as required by the SONATA specification."""
|
|
859
|
+
output_path = self.circuit_access.config.spikes_file_path
|
|
860
|
+
|
|
861
|
+
if os.path.exists(output_path):
|
|
862
|
+
os.remove(output_path)
|
|
863
|
+
|
|
864
|
+
# Group spikes per population
|
|
865
|
+
spikes_by_population = defaultdict(dict)
|
|
866
|
+
for gid, cell in self.cells.items():
|
|
867
|
+
pop = getattr(gid, 'population_name', None)
|
|
868
|
+
if pop is None:
|
|
869
|
+
continue
|
|
870
|
+
try:
|
|
871
|
+
cell_spikes = cell.get_recorded_spikes(location="soma", threshold=self.spike_threshold)
|
|
872
|
+
if cell_spikes is not None:
|
|
873
|
+
spikes_by_population[pop][gid.id] = list(cell_spikes)
|
|
874
|
+
except AttributeError:
|
|
875
|
+
continue
|
|
876
|
+
|
|
877
|
+
# Ensure we at least create empty groups for all known populations
|
|
878
|
+
all_populations = set(getattr(gid, 'population_name', None) for gid in self.cells.keys())
|
|
879
|
+
|
|
880
|
+
for pop in all_populations:
|
|
881
|
+
spikes = spikes_by_population.get(pop, {}) # May be empty
|
|
882
|
+
write_sonata_spikes(output_path, spikes, pop)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Copyright 2025 Open Brain Institute
|
|
2
|
+
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""Report class of bluecellulab."""
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
import h5py
|
|
19
|
+
from typing import List
|
|
20
|
+
import numpy as np
|
|
21
|
+
import os
|
|
22
|
+
|
|
23
|
+
from bluecellulab.tools import resolve_segments, resolve_source_nodes
|
|
24
|
+
from bluecellulab.cell.cell_dict import CellDict
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _configure_recording(cell, report_cfg, source, source_type, report_name):
|
|
30
|
+
variable = report_cfg.get("variable_name", "v")
|
|
31
|
+
|
|
32
|
+
node_id = cell.cell_id
|
|
33
|
+
compartment_nodes = source.get("compartment_set") if source_type == "compartment_set" else None
|
|
34
|
+
|
|
35
|
+
targets = resolve_segments(cell, report_cfg, node_id, compartment_nodes, source_type)
|
|
36
|
+
for sec, sec_name, seg in targets:
|
|
37
|
+
try:
|
|
38
|
+
cell.add_variable_recording(variable=variable, section=sec, segx=seg)
|
|
39
|
+
except AttributeError:
|
|
40
|
+
logger.warning(f"Recording for variable '{variable}' is not implemented in Cell.")
|
|
41
|
+
return
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logger.warning(
|
|
44
|
+
f"Failed to record '{variable}' at {sec_name}({seg}) on GID {node_id} for report '{report_name}': {e}"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def configure_all_reports(cells, simulation_config):
|
|
49
|
+
report_entries = simulation_config.get_report_entries()
|
|
50
|
+
|
|
51
|
+
for report_name, report_cfg in report_entries.items():
|
|
52
|
+
report_type = report_cfg.get("type", "compartment")
|
|
53
|
+
section = report_cfg.get("sections", "soma")
|
|
54
|
+
|
|
55
|
+
if report_type != "compartment":
|
|
56
|
+
raise NotImplementedError(f"Report type '{report_type}' is not supported.")
|
|
57
|
+
|
|
58
|
+
if section == "compartment_set":
|
|
59
|
+
source_type = "compartment_set"
|
|
60
|
+
source_sets = simulation_config.get_compartment_sets()
|
|
61
|
+
source_name = report_cfg.get("compartments")
|
|
62
|
+
if not source_name:
|
|
63
|
+
logger.warning(f"Report '{report_name}' does not specify a node set in 'compartments' for {source_type}.")
|
|
64
|
+
continue
|
|
65
|
+
else:
|
|
66
|
+
source_type = "node_set"
|
|
67
|
+
source_sets = simulation_config.get_node_sets()
|
|
68
|
+
source_name = report_cfg.get("cells")
|
|
69
|
+
if not source_name:
|
|
70
|
+
logger.warning(f"Report '{report_name}' does not specify a node set in 'cells' for {source_type}.")
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
source = source_sets.get(source_name)
|
|
74
|
+
if not source:
|
|
75
|
+
logger.warning(f"{source_type.title()} '{source_name}' not found for report '{report_name}', skipping recording.")
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
population = source["population"]
|
|
79
|
+
node_ids, _ = resolve_source_nodes(source, source_type, cells, population)
|
|
80
|
+
|
|
81
|
+
for node_id in node_ids:
|
|
82
|
+
cell = cells.get((population, node_id))
|
|
83
|
+
if not cell:
|
|
84
|
+
continue
|
|
85
|
+
_configure_recording(cell, report_cfg, source, source_type, report_name)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def write_compartment_report(
|
|
89
|
+
report_name: str,
|
|
90
|
+
output_path: str,
|
|
91
|
+
cells: CellDict,
|
|
92
|
+
report_cfg: dict,
|
|
93
|
+
source_sets: dict,
|
|
94
|
+
source_type: str,
|
|
95
|
+
):
|
|
96
|
+
"""Write a SONATA-compatible compartment report to an HDF5 file.
|
|
97
|
+
|
|
98
|
+
This function collects time series data (e.g., membrane voltage, ion currents)
|
|
99
|
+
from a group of cells defined by either a node set or a compartment set, and
|
|
100
|
+
writes the data to a SONATA-style report file.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
output_path (str): Path to the output HDF5 file.
|
|
104
|
+
cells (CellDict): Mapping of (population, node_id) to cell objects that
|
|
105
|
+
provide access to pre-recorded variable traces.
|
|
106
|
+
report_cfg (dict): Configuration for the report. Must include:
|
|
107
|
+
- "variable_name": Name of the variable to report (e.g., "v", "ica", "ina").
|
|
108
|
+
- "start_time", "end_time", "dt": Timing parameters.
|
|
109
|
+
- "cells" or "compartments": Name of the node or compartment set.
|
|
110
|
+
source_sets (dict): Dictionary of either node sets or compartment sets.
|
|
111
|
+
source_type (str): Either "node_set" or "compartment_set".
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
ValueError: If the specified source set is not found.
|
|
115
|
+
|
|
116
|
+
Notes:
|
|
117
|
+
- Currently supports only variables explicitly handled in Cell.get_variable_recording().
|
|
118
|
+
- Cells without recordings for the requested variable will be skipped.
|
|
119
|
+
"""
|
|
120
|
+
source_name = report_cfg.get("cells") if source_type == "node_set" else report_cfg.get("compartments")
|
|
121
|
+
source = source_sets.get(source_name)
|
|
122
|
+
if not source:
|
|
123
|
+
logger.warning(f"{source_type.title()} '{source_name}' not found for report '{report_name}', skipping write.")
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
population = source["population"]
|
|
127
|
+
|
|
128
|
+
node_ids, compartment_nodes = resolve_source_nodes(source, source_type, cells, population)
|
|
129
|
+
|
|
130
|
+
data_matrix: List[np.ndarray] = []
|
|
131
|
+
recorded_node_ids: List[int] = []
|
|
132
|
+
index_pointers: List[int] = [0]
|
|
133
|
+
element_ids: List[int] = []
|
|
134
|
+
|
|
135
|
+
for node_id in node_ids:
|
|
136
|
+
try:
|
|
137
|
+
cell = cells[(population, node_id)]
|
|
138
|
+
except KeyError:
|
|
139
|
+
continue
|
|
140
|
+
if not cell:
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
targets = resolve_segments(cell, report_cfg, node_id, compartment_nodes, source_type)
|
|
144
|
+
for sec, sec_name, seg in targets:
|
|
145
|
+
try:
|
|
146
|
+
variable = report_cfg.get("variable_name", "v")
|
|
147
|
+
trace = cell.get_variable_recording(variable=variable, section=sec, segx=seg)
|
|
148
|
+
data_matrix.append(trace)
|
|
149
|
+
recorded_node_ids.append(node_id)
|
|
150
|
+
element_ids.append(len(element_ids))
|
|
151
|
+
index_pointers.append(index_pointers[-1] + 1)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.warning(f"Failed recording: GID {node_id} sec {sec_name} seg {seg}: {e}")
|
|
154
|
+
|
|
155
|
+
if not data_matrix:
|
|
156
|
+
logger.warning(f"No data recorded for report '{source_name}'. Skipping write.")
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
write_sonata_report_file(
|
|
160
|
+
output_path, population, data_matrix, recorded_node_ids, index_pointers, element_ids, report_cfg
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def write_sonata_report_file(
|
|
165
|
+
output_path, population, data_matrix, recorded_node_ids, index_pointers, element_ids, report_cfg
|
|
166
|
+
):
|
|
167
|
+
data_array = np.stack(data_matrix, axis=1)
|
|
168
|
+
node_ids_arr = np.array(recorded_node_ids, dtype=np.uint64)
|
|
169
|
+
index_ptr_arr = np.array(index_pointers, dtype=np.uint64)
|
|
170
|
+
element_ids_arr = np.array(element_ids, dtype=np.uint32)
|
|
171
|
+
time_array = np.array([
|
|
172
|
+
report_cfg.get("start_time", 0.0),
|
|
173
|
+
report_cfg.get("end_time", 0.0),
|
|
174
|
+
report_cfg.get("dt", 0.1)
|
|
175
|
+
], dtype=np.float64)
|
|
176
|
+
|
|
177
|
+
output_path = Path(output_path)
|
|
178
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
with h5py.File(output_path, "w") as f:
|
|
180
|
+
grp = f.require_group(f"/report/{population}")
|
|
181
|
+
data_ds = grp.create_dataset("data", data=data_array.astype(np.float32))
|
|
182
|
+
|
|
183
|
+
variable = report_cfg.get("variable_name", "v")
|
|
184
|
+
if variable == "v":
|
|
185
|
+
data_ds.attrs["units"] = "mV"
|
|
186
|
+
|
|
187
|
+
mapping = grp.require_group("mapping")
|
|
188
|
+
mapping.create_dataset("node_ids", data=node_ids_arr)
|
|
189
|
+
mapping.create_dataset("index_pointers", data=index_ptr_arr)
|
|
190
|
+
mapping.create_dataset("element_ids", data=element_ids_arr)
|
|
191
|
+
time_ds = mapping.create_dataset("time", data=time_array)
|
|
192
|
+
time_ds.attrs["units"] = "ms"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def write_sonata_spikes(f_name: str, spikes_dict: dict[int, np.ndarray], population: str):
|
|
196
|
+
"""Write a SONATA spike group to a spike file from {node_id: [t1, t2,
|
|
197
|
+
...]}."""
|
|
198
|
+
all_node_ids: List[int] = []
|
|
199
|
+
all_timestamps: List[float] = []
|
|
200
|
+
|
|
201
|
+
for node_id, times in spikes_dict.items():
|
|
202
|
+
all_node_ids.extend([node_id] * len(times))
|
|
203
|
+
all_timestamps.extend(times)
|
|
204
|
+
|
|
205
|
+
if not all_timestamps:
|
|
206
|
+
logger.warning(f"No spikes to write for population '{population}'.")
|
|
207
|
+
|
|
208
|
+
# Sort by time for consistency
|
|
209
|
+
sorted_indices = np.argsort(all_timestamps)
|
|
210
|
+
node_ids_sorted = np.array(all_node_ids, dtype=np.uint64)[sorted_indices]
|
|
211
|
+
timestamps_sorted = np.array(all_timestamps, dtype=np.float64)[sorted_indices]
|
|
212
|
+
|
|
213
|
+
os.makedirs(os.path.dirname(f_name), exist_ok=True)
|
|
214
|
+
with h5py.File(f_name, 'a') as f: # 'a' to allow multiple writes
|
|
215
|
+
spikes_group = f.require_group("spikes")
|
|
216
|
+
if population in spikes_group:
|
|
217
|
+
logger.warning(f"Overwriting existing group for population '{population}' in {f_name}.")
|
|
218
|
+
del spikes_group[population]
|
|
219
|
+
|
|
220
|
+
group = spikes_group.create_group(population)
|
|
221
|
+
sorting_enum = h5py.enum_dtype({'none': 0, 'by_id': 1, 'by_time': 2}, basetype='u1')
|
|
222
|
+
group.attrs.create("sorting", 2, dtype=sorting_enum) # 2 = by_time
|
|
223
|
+
|
|
224
|
+
timestamps_ds = group.create_dataset("timestamps", data=timestamps_sorted)
|
|
225
|
+
group.create_dataset("node_ids", data=node_ids_sorted)
|
|
226
|
+
|
|
227
|
+
timestamps_ds.attrs["units"] = "ms" # SONATA-required
|
|
@@ -394,14 +394,14 @@ def check_empty_topology() -> bool:
|
|
|
394
394
|
|
|
395
395
|
|
|
396
396
|
def calculate_max_thresh_current(cell: Cell,
|
|
397
|
-
threshold_voltage: float = -
|
|
397
|
+
threshold_voltage: float = -20.0,
|
|
398
398
|
section: str = "soma[0]",
|
|
399
399
|
segx: float = 0.5) -> float:
|
|
400
400
|
"""Calculate the upper bound threshold current.
|
|
401
401
|
|
|
402
402
|
Args:
|
|
403
403
|
cell (bluecellulab.cell.Cell): The initialized cell model.
|
|
404
|
-
threshold_voltage (float, optional): Voltage threshold for spike detection. Default is -
|
|
404
|
+
threshold_voltage (float, optional): Voltage threshold for spike detection. Default is -20.0 mV.
|
|
405
405
|
section (str, optional): The section where current is injected.
|
|
406
406
|
segx (float, optional): Fractional location within the section for current injection.
|
|
407
407
|
|
|
@@ -508,19 +508,85 @@ def validate_section_and_segment(cell: Cell, section_name: str, segment_position
|
|
|
508
508
|
|
|
509
509
|
|
|
510
510
|
def get_section(cell: Cell, section_name: str) -> NeuronSection:
|
|
511
|
-
"""
|
|
511
|
+
"""Return a single, fully specified NEURON section (e.g., 'soma[0]',
|
|
512
|
+
'dend[3]').
|
|
512
513
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
514
|
+
Raises:
|
|
515
|
+
ValueError or TypeError if the section is not found or invalid.
|
|
516
|
+
"""
|
|
517
|
+
if section_name in cell.sections:
|
|
518
|
+
section = cell.sections[section_name]
|
|
519
|
+
if hasattr(section, "nseg"):
|
|
520
|
+
return section
|
|
521
|
+
raise TypeError(f"'{section_name}' exists but is not a NEURON section.")
|
|
522
|
+
|
|
523
|
+
available = ", ".join(cell.sections.keys())
|
|
524
|
+
raise ValueError(f"Section '{section_name}' not found. Available: [{available}]")
|
|
516
525
|
|
|
517
|
-
|
|
518
|
-
|
|
526
|
+
|
|
527
|
+
def get_sections(cell, section_name: str):
|
|
528
|
+
"""Return a list of NEURON sections.
|
|
529
|
+
|
|
530
|
+
If the section name is a fully specified one (e.g., 'dend[3]'), return it as a list of one.
|
|
531
|
+
If the section name is a base name (e.g., 'dend'), return all matching sections like 'dend[0]', 'dend[1]', etc.
|
|
519
532
|
|
|
520
533
|
Raises:
|
|
521
|
-
ValueError
|
|
534
|
+
ValueError or TypeError if no valid sections are found.
|
|
522
535
|
"""
|
|
536
|
+
# Try to interpret as fully qualified section name
|
|
523
537
|
try:
|
|
524
|
-
return cell
|
|
525
|
-
except
|
|
526
|
-
|
|
538
|
+
return [get_section(cell, section_name)]
|
|
539
|
+
except ValueError:
|
|
540
|
+
pass # Not a precise match; try prefix match
|
|
541
|
+
|
|
542
|
+
# Fallback to prefix-based match (e.g., 'dend' → 'dend[0]', 'dend[1]', ...)
|
|
543
|
+
matched = [
|
|
544
|
+
section for name, section in cell.sections.items()
|
|
545
|
+
if name.startswith(f"{section_name}[")
|
|
546
|
+
]
|
|
547
|
+
if matched:
|
|
548
|
+
return matched
|
|
549
|
+
|
|
550
|
+
available = ", ".join(cell.sections.keys())
|
|
551
|
+
raise ValueError(f"Section '{section_name}' not found. Available: [{available}]")
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def resolve_segments(cell, report_cfg, node_id, compartment_nodes, source_type):
|
|
555
|
+
"""Determine which segments to record from one or more NEURON sections."""
|
|
556
|
+
section_name = report_cfg.get("sections", "soma")
|
|
557
|
+
compartment = report_cfg.get("compartments", "center")
|
|
558
|
+
|
|
559
|
+
if source_type == "compartment_set":
|
|
560
|
+
return [
|
|
561
|
+
(get_section(cell, sec), sec, seg)
|
|
562
|
+
for _, sec, seg in compartment_nodes if _ == node_id
|
|
563
|
+
]
|
|
564
|
+
|
|
565
|
+
sections = get_sections(cell, section_name)
|
|
566
|
+
targets = []
|
|
567
|
+
|
|
568
|
+
for sec in sections:
|
|
569
|
+
sec_name = sec.name().split(".")[-1]
|
|
570
|
+
if compartment == "center":
|
|
571
|
+
targets.append((sec, sec_name, 0.5))
|
|
572
|
+
elif compartment == "all":
|
|
573
|
+
for seg in sec:
|
|
574
|
+
targets.append((sec, sec_name, seg.x))
|
|
575
|
+
else:
|
|
576
|
+
raise ValueError(
|
|
577
|
+
f"Unsupported 'compartments' value '{compartment}' — must be 'center' or 'all'."
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
return targets
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def resolve_source_nodes(source, source_type, cells, population):
|
|
584
|
+
if source_type == "compartment_set":
|
|
585
|
+
compartment_nodes = source.get("compartment_set", [])
|
|
586
|
+
node_ids = [entry[0] for entry in compartment_nodes]
|
|
587
|
+
else: # node_set
|
|
588
|
+
node_ids = source.get("node_id")
|
|
589
|
+
if node_ids is None:
|
|
590
|
+
node_ids = [node_id for (pop, node_id) in cells.keys() if pop == population]
|
|
591
|
+
compartment_nodes = None
|
|
592
|
+
return node_ids, compartment_nodes
|
|
@@ -82,6 +82,7 @@ bluecellulab/hoc/fileUtils.hoc
|
|
|
82
82
|
bluecellulab/simulation/__init__.py
|
|
83
83
|
bluecellulab/simulation/neuron_globals.py
|
|
84
84
|
bluecellulab/simulation/parallel.py
|
|
85
|
+
bluecellulab/simulation/report.py
|
|
85
86
|
bluecellulab/simulation/simulation.py
|
|
86
87
|
bluecellulab/stimulus/__init__.py
|
|
87
88
|
bluecellulab/stimulus/circuit_stimulus_definitions.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/circuit_access/definition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/circuit/config/bluepy_simulation_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bluecellulab-2.6.51 → bluecellulab-2.6.52}/bluecellulab/stimulus/circuit_stimulus_definitions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|