bluerecording 0.4.0__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.
- bluerecording-0.4.0/.gitignore +58 -0
- bluerecording-0.4.0/PKG-INFO +31 -0
- bluerecording-0.4.0/bluerecording/__init__.py +40 -0
- bluerecording-0.4.0/bluerecording/circuit.py +68 -0
- bluerecording-0.4.0/bluerecording/cli.py +131 -0
- bluerecording-0.4.0/bluerecording/positions.py +600 -0
- bluerecording-0.4.0/bluerecording/utils.py +20 -0
- bluerecording-0.4.0/bluerecording/weights.py +893 -0
- bluerecording-0.4.0/pyproject.toml +70 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
x86_64/
|
|
2
|
+
**/core*
|
|
3
|
+
figures/
|
|
4
|
+
positions_all_new/
|
|
5
|
+
positions_all_new_big/
|
|
6
|
+
**/*coeffs*.h5
|
|
7
|
+
*.SUCCESS
|
|
8
|
+
networks/
|
|
9
|
+
!examples/compare-to-reference-solutions_old/data/configuration/networks/
|
|
10
|
+
!examples/single_cell_l5_tpc/configuration/networks/
|
|
11
|
+
network/
|
|
12
|
+
examples/allen/data/simulation/configuration/components
|
|
13
|
+
bkg_inputs/
|
|
14
|
+
atlas/
|
|
15
|
+
reporting/
|
|
16
|
+
!examples/compare-to-reference-solutions_old/data/reporting/
|
|
17
|
+
reporting_small/
|
|
18
|
+
reporting_big/
|
|
19
|
+
output_sonata/
|
|
20
|
+
**/*.png
|
|
21
|
+
**/*.pyc
|
|
22
|
+
**/*.log
|
|
23
|
+
**/*.out
|
|
24
|
+
mod/
|
|
25
|
+
.ipynb_checkpoints/
|
|
26
|
+
**/*.dat
|
|
27
|
+
**/*.swp
|
|
28
|
+
**/*.swn
|
|
29
|
+
**/*.swo
|
|
30
|
+
**/*.err
|
|
31
|
+
build/
|
|
32
|
+
bluerecording.egg-info
|
|
33
|
+
.exception_node
|
|
34
|
+
*.nrrd
|
|
35
|
+
neurodamus/
|
|
36
|
+
positions/
|
|
37
|
+
*.pkl
|
|
38
|
+
!*_ref.pkl
|
|
39
|
+
venv/
|
|
40
|
+
*_venv/
|
|
41
|
+
env.sh
|
|
42
|
+
pkls/
|
|
43
|
+
pkls_big/
|
|
44
|
+
htmlcov/
|
|
45
|
+
**/EEG.h5
|
|
46
|
+
**/ECoG.h5
|
|
47
|
+
**/LFP.h5
|
|
48
|
+
*removeme*
|
|
49
|
+
*DS_Store
|
|
50
|
+
*Infinite_VeryFar_HighRes.h5
|
|
51
|
+
*Infinite_Close_HighRes_SmallSphere.h5
|
|
52
|
+
libsonatareport/
|
|
53
|
+
neurodamus-models/
|
|
54
|
+
nrn/
|
|
55
|
+
.code
|
|
56
|
+
.kiro
|
|
57
|
+
*output/
|
|
58
|
+
.vscode/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bluerecording
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A tool for calculating extracellular recording lead fields
|
|
5
|
+
Requires-Python: <3.14,>=3.10
|
|
6
|
+
Requires-Dist: h5py
|
|
7
|
+
Requires-Dist: libsonata
|
|
8
|
+
Requires-Dist: morphio
|
|
9
|
+
Requires-Dist: mpi4py
|
|
10
|
+
Requires-Dist: neurodamus>=4.2.1
|
|
11
|
+
Requires-Dist: pandas
|
|
12
|
+
Requires-Dist: scikit-learn
|
|
13
|
+
Requires-Dist: voxcell
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: bluepysnap; extra == 'all'
|
|
16
|
+
Requires-Dist: ipympl; extra == 'all'
|
|
17
|
+
Requires-Dist: jupyterlab; extra == 'all'
|
|
18
|
+
Requires-Dist: neuron; extra == 'all'
|
|
19
|
+
Requires-Dist: pytest; extra == 'all'
|
|
20
|
+
Requires-Dist: pytest-forked; extra == 'all'
|
|
21
|
+
Requires-Dist: pytest-mpi; extra == 'all'
|
|
22
|
+
Provides-Extra: neuron
|
|
23
|
+
Requires-Dist: neuron; extra == 'neuron'
|
|
24
|
+
Provides-Extra: notebooks
|
|
25
|
+
Requires-Dist: bluepysnap; extra == 'notebooks'
|
|
26
|
+
Requires-Dist: ipympl; extra == 'notebooks'
|
|
27
|
+
Requires-Dist: jupyterlab; extra == 'notebooks'
|
|
28
|
+
Provides-Extra: test
|
|
29
|
+
Requires-Dist: pytest; extra == 'test'
|
|
30
|
+
Requires-Dist: pytest-forked; extra == 'test'
|
|
31
|
+
Requires-Dist: pytest-mpi; extra == 'test'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
|
|
4
|
+
__version__ = version("bluerecording")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _check_dependencies():
|
|
8
|
+
"""Verify runtime dependencies that need special attention.
|
|
9
|
+
|
|
10
|
+
- h5py is declared in pyproject.toml but must be the MPI-enabled build.
|
|
11
|
+
The default pip wheel lacks MPI support.
|
|
12
|
+
- neuron is an optional extra ([neuron]). Required at runtime but kept
|
|
13
|
+
optional because it may be built from source.
|
|
14
|
+
"""
|
|
15
|
+
import h5py
|
|
16
|
+
|
|
17
|
+
if not h5py.get_config().mpi:
|
|
18
|
+
raise ImportError(
|
|
19
|
+
"h5py is installed but was built without MPI support.\n"
|
|
20
|
+
"bluerecording requires parallel HDF5 I/O.\n"
|
|
21
|
+
"Fix with:\n"
|
|
22
|
+
" pip uninstall h5py\n"
|
|
23
|
+
" HDF5_MPI=ON pip install --no-cache-dir --no-binary=h5py h5py "
|
|
24
|
+
"--no-build-isolation\n"
|
|
25
|
+
"Or use './dev_setup.sh' followed by 'source env.sh' "
|
|
26
|
+
"which handles this automatically."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import neuron # noqa: F401
|
|
31
|
+
except ImportError:
|
|
32
|
+
raise ImportError(
|
|
33
|
+
"bluerecording requires NEURON.\n"
|
|
34
|
+
"Install with: pip install bluerecording[neuron]\n"
|
|
35
|
+
"Or use './dev_setup.sh' followed by 'source env.sh' "
|
|
36
|
+
"to build from source."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_check_dependencies()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Shared circuit initialization via neurodamus.
|
|
3
|
+
|
|
4
|
+
Provides the entry point for loading a circuit model and extracting
|
|
5
|
+
the discretization info (node IDs, compartment structure, morphology access)
|
|
6
|
+
needed by both get_positions and write_weights.
|
|
7
|
+
"""
|
|
8
|
+
import libsonata
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from .utils import get_circuit_path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_circuit(path_to_simconfig: str):
|
|
15
|
+
"""Initialize neurodamus and extract circuit discretization info.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
path_to_simconfig: Path to the SONATA simulation configuration file.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
node_manager: The neurodamus node manager for the single population.
|
|
22
|
+
ids: GIDs assigned to this MPI rank.
|
|
23
|
+
cols: (N, 2) int64 array of (gid, section) pairs describing every
|
|
24
|
+
compartment on this rank.
|
|
25
|
+
population: libsonata NodePopulation, needed for morphology file
|
|
26
|
+
resolution.
|
|
27
|
+
population_name: Name of the SONATA node population.
|
|
28
|
+
morphologies_dir: Fully resolved path to the morphologies directory,
|
|
29
|
+
as provided by libsonata.
|
|
30
|
+
"""
|
|
31
|
+
# Lazy import: neurodamus pulls in NEURON, which is not available
|
|
32
|
+
# in lightweight installs (e.g. CI with --quick).
|
|
33
|
+
import neurodamus
|
|
34
|
+
|
|
35
|
+
nd = neurodamus.Neurodamus(
|
|
36
|
+
path_to_simconfig,
|
|
37
|
+
disable_reports=True,
|
|
38
|
+
direct_mode=True,
|
|
39
|
+
build_model=True,
|
|
40
|
+
enable_coord_mapping=True,
|
|
41
|
+
keep_build=False,
|
|
42
|
+
simulator="NEURON",
|
|
43
|
+
)
|
|
44
|
+
assert len(nd.circuits.node_managers) == 1, (
|
|
45
|
+
"Multiple or no node managers are not allowed for the moment"
|
|
46
|
+
)
|
|
47
|
+
node_manager = next(iter(nd.circuits.node_managers.values()))
|
|
48
|
+
|
|
49
|
+
ids = node_manager.get_final_gids()
|
|
50
|
+
points = node_manager.target_manager.get_target(None).get_point_list(
|
|
51
|
+
node_manager,
|
|
52
|
+
libsonata.SimulationConfig.Report.Sections.all,
|
|
53
|
+
libsonata.SimulationConfig.Report.Compartments.all,
|
|
54
|
+
)
|
|
55
|
+
cols = np.array(
|
|
56
|
+
[(p.gid, s) for p in points for s in sorted(p.sclst_ids)],
|
|
57
|
+
dtype=np.int64,
|
|
58
|
+
).reshape(-1, 2)
|
|
59
|
+
|
|
60
|
+
population_name = node_manager.population_name
|
|
61
|
+
|
|
62
|
+
circuit_conf = libsonata.CircuitConfig.from_file(
|
|
63
|
+
get_circuit_path(path_to_simconfig)
|
|
64
|
+
)
|
|
65
|
+
population = circuit_conf.node_population(population_name)
|
|
66
|
+
morphologies_dir = circuit_conf.node_population_properties(population_name).morphologies_dir
|
|
67
|
+
|
|
68
|
+
return node_manager, ids, cols, population, population_name, morphologies_dir
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from . import positions
|
|
4
|
+
from .circuit import init_circuit
|
|
5
|
+
from .weights import DEFAULT_SIGMA, write_h5_file
|
|
6
|
+
from .weights import initialize_h5_file
|
|
7
|
+
from . import __version__
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
prog="bluerecording",
|
|
12
|
+
description="Bluerecording CLI"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"--version",
|
|
17
|
+
action="version",
|
|
18
|
+
version=f"%(prog)s {__version__}"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
22
|
+
|
|
23
|
+
# write_positions command
|
|
24
|
+
gp_parser = subparsers.add_parser(
|
|
25
|
+
"write_positions",
|
|
26
|
+
help="Compute and save segment positions to disk"
|
|
27
|
+
)
|
|
28
|
+
gp_parser.add_argument(
|
|
29
|
+
"path_to_simconfig",
|
|
30
|
+
type=str,
|
|
31
|
+
help="Path to the simulation configuration file"
|
|
32
|
+
)
|
|
33
|
+
gp_parser.add_argument(
|
|
34
|
+
"path_to_positions_folder",
|
|
35
|
+
type=str,
|
|
36
|
+
help="Path to the folder where positions will be stored"
|
|
37
|
+
)
|
|
38
|
+
gp_parser.add_argument(
|
|
39
|
+
"--no-replace-axons",
|
|
40
|
+
action="store_false",
|
|
41
|
+
dest="replace_axons",
|
|
42
|
+
help="Do not replace existing axons (default: replace)"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# write_weights command
|
|
46
|
+
ww_parser = subparsers.add_parser(
|
|
47
|
+
"write_weights",
|
|
48
|
+
help="Compute electrode weights for all cells in the circuit"
|
|
49
|
+
)
|
|
50
|
+
ww_parser.add_argument(
|
|
51
|
+
"path_to_simconfig",
|
|
52
|
+
type=str,
|
|
53
|
+
help="Path to the simulation configuration file"
|
|
54
|
+
)
|
|
55
|
+
ww_parser.add_argument(
|
|
56
|
+
"electrode_csv",
|
|
57
|
+
type=str,
|
|
58
|
+
help="Path to the electrode CSV file"
|
|
59
|
+
)
|
|
60
|
+
ww_parser.add_argument(
|
|
61
|
+
"output_path",
|
|
62
|
+
type=str,
|
|
63
|
+
help="Path to the output H5 weights file, or a directory (weights.h5 will be created inside)"
|
|
64
|
+
)
|
|
65
|
+
ww_parser.add_argument(
|
|
66
|
+
"--no-replace-axons",
|
|
67
|
+
action="store_false",
|
|
68
|
+
dest="replace_axons",
|
|
69
|
+
help="Do not replace existing axons (default: replace)"
|
|
70
|
+
)
|
|
71
|
+
ww_parser.add_argument(
|
|
72
|
+
"--sigma",
|
|
73
|
+
type=float,
|
|
74
|
+
nargs="+",
|
|
75
|
+
default=None,
|
|
76
|
+
help=f"Extracellular conductivity in S/m (default: {DEFAULT_SIGMA})"
|
|
77
|
+
)
|
|
78
|
+
ww_parser.add_argument(
|
|
79
|
+
"--path-to-fields",
|
|
80
|
+
type=str,
|
|
81
|
+
nargs="+",
|
|
82
|
+
default=None,
|
|
83
|
+
help="Path(s) to H5 potential field files for reciprocity electrodes"
|
|
84
|
+
)
|
|
85
|
+
ww_parser.add_argument(
|
|
86
|
+
"--with-neurite-type",
|
|
87
|
+
action="store_true",
|
|
88
|
+
default=False,
|
|
89
|
+
dest="with_neurite_type",
|
|
90
|
+
help="Append a neurite_types dataset to the weights file",
|
|
91
|
+
)
|
|
92
|
+
ww_parser.add_argument(
|
|
93
|
+
"--write-positions",
|
|
94
|
+
action="store_true",
|
|
95
|
+
default=False,
|
|
96
|
+
dest="write_positions",
|
|
97
|
+
help="Also save segment positions alongside the weights file",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
args = parser.parse_args()
|
|
101
|
+
|
|
102
|
+
if args.command == "write_positions":
|
|
103
|
+
node_manager, ids, cols, population, _, morphologies_dir = init_circuit(args.path_to_simconfig)
|
|
104
|
+
positions_df, _, _ = positions.get_positions(
|
|
105
|
+
node_manager, ids, cols, population,
|
|
106
|
+
morphologies_dir=morphologies_dir,
|
|
107
|
+
replace_axons=args.replace_axons,
|
|
108
|
+
)
|
|
109
|
+
positions.save_positions(positions_df, args.path_to_positions_folder)
|
|
110
|
+
|
|
111
|
+
elif args.command == "write_weights":
|
|
112
|
+
node_manager, ids, cols, population, population_name, morphologies_dir = init_circuit(args.path_to_simconfig)
|
|
113
|
+
positions_df, cols, neurite_types = positions.get_positions(
|
|
114
|
+
node_manager, ids, cols, population,
|
|
115
|
+
morphologies_dir=morphologies_dir,
|
|
116
|
+
replace_axons=args.replace_axons,
|
|
117
|
+
)
|
|
118
|
+
output_file = Path(args.output_path)
|
|
119
|
+
if output_file.is_dir() or not output_file.suffix:
|
|
120
|
+
output_file.mkdir(parents=True, exist_ok=True)
|
|
121
|
+
output_file = output_file / "weights.h5"
|
|
122
|
+
elif output_file.suffix != ".h5":
|
|
123
|
+
parser.error(f"output_path must be a directory or an .h5 file, got '{output_file}'")
|
|
124
|
+
|
|
125
|
+
initialize_h5_file(cols, population_name, str(output_file), args.electrode_csv,
|
|
126
|
+
with_neurite_type=args.with_neurite_type)
|
|
127
|
+
write_h5_file(positions_df, cols, population_name, str(output_file),
|
|
128
|
+
sigma=args.sigma, path_to_fields=args.path_to_fields,
|
|
129
|
+
neurite_types=neurite_types if args.with_neurite_type else None)
|
|
130
|
+
if args.write_positions:
|
|
131
|
+
positions.save_positions(positions_df, output_file.parent)
|