Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
129cba2
fix: make flush method to save in file
Jan 16, 2026
afc9711
Merge branch 'main' into fix/checkpoint_flush
filimarc Jan 19, 2026
98f2e06
fix: filename options and sim tests
filimarc Jan 19, 2026
3ae4746
fix: backward compatibility
filimarc Jan 20, 2026
92d2334
Merge branch 'main' into fix/checkpoint_flush
filimarc Jan 27, 2026
7fb5403
Merge branch 'main' into fix/checkpoint_flush
filimarc Feb 25, 2026
895f701
Update packages/bsb-core/bsb/cli/commands/_commands.py
filimarc Mar 25, 2026
99db5fd
Merge branch 'main' into fix/checkpoint_flush
filimarc Apr 13, 2026
304b83f
test: add double simulation test
filimarc Apr 13, 2026
3a527fa
test: add test for RAM usage
Apr 14, 2026
847ec02
fix: install-nest.sh
filimarc Apr 15, 2026
34e878f
test: fix test for RAM usage
Apr 16, 2026
bbe3814
fix: nest installation issue
Apr 16, 2026
d2a04ae
fix: test with MPI
Apr 16, 2026
cdf7f71
Merge branch 'main' into fix/checkpoint_flush
filimarc Apr 17, 2026
3f21916
chore: lint files
filimarc Apr 17, 2026
b4ee547
Merge branch 'main' into fix/checkpoint_flush
filimarc May 8, 2026
b84664a
fix: update to new nest 3.10
filimarc May 8, 2026
7be391d
chore: Merge remote-tracking branch origin/fix/checkpoint_flush
filimarc May 8, 2026
a830db7
fix: add check on nest version
filimarc May 11, 2026
36898eb
fix: add a third sim to the test
filimarc May 11, 2026
bbfa58c
fix: add test for RAM usage in bsb-core | add alternatives for analog…
May 13, 2026
f28891c
fix: remove test in bsb-neuron
May 13, 2026
2ced635
fix: lint files
May 13, 2026
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
4 changes: 2 additions & 2 deletions devtools/install-nest.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Get Nest installation folder
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"
if [ -z "$NEST_FOLDER" ]; then
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
NEST_FOLDER="$(dirname $SCRIPT_DIR)/.nx/installation/nest";
fi
# Get NEST version
Expand All @@ -13,7 +13,7 @@ if [ -f "$INSTALLATION_FOLDER/bin/nest_vars.sh" ]; then
fi

# Lock check and installation to prevent concurrent file edition
LOCK_FILE="/tmp/bsb-nest.lock"
LOCK_FILE="$SCRIPT_DIR/bsb-nest.lock"
# Remove lock file on exit
trap 'rm -f "$LOCK_FILE"' EXIT
# Wait to be able to create file
Expand Down
4 changes: 2 additions & 2 deletions examples/nest-simulation/tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_json_example(self):
self.scaffold.compile()
self._test_scaffold_results()
results = self.scaffold.run_simulation("basal_activity")
self._test_simulation_results(results.spiketrains)
self._test_simulation_results(results.block.segments[0].spiketrains)

def test_yaml_example(self):
self.cfg = parse_configuration_file(
Expand All @@ -72,7 +72,7 @@ def test_yaml_example(self):
self.scaffold.compile()
self._test_scaffold_results()
results = self.scaffold.run_simulation("basal_activity")
self._test_simulation_results(results.spiketrains)
self._test_simulation_results(results.block.segments[0].spiketrains)

def test_python_example(self):
import scripts.guide_nest # noqa: F401
Expand Down
4 changes: 2 additions & 2 deletions examples/neuron-simulation/tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_json_example(self):
self.scaffold.compile()
self._test_scaffold_results()
results = self.scaffold.run_simulation("neuronsim")
self._test_simulation_results(results.analogsignals)
self._test_simulation_results(results.block.segments[0].analogsignals)

def test_yaml_example(self):
self.cfg = parse_configuration_file(
Expand All @@ -77,7 +77,7 @@ def test_yaml_example(self):
self.scaffold.compile()
self._test_scaffold_results()
results = self.scaffold.run_simulation("neuronsim")
self._test_simulation_results(results.analogsignals)
self._test_simulation_results(results.block.segments[0].analogsignals)

def test_python_example(self):
import scripts.guide_neuron # noqa: F401
Expand Down
14 changes: 8 additions & 6 deletions packages/bsb-arbor/bsb_arbor/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ class ArborSimulationData(SimulationData):
Container class for simulation data.
"""

def __init__(self, simulation):
def __init__(self, simulation, filename):
"""
Container class for simulation data.
"""
super().__init__(simulation)
super().__init__(simulation, filename=filename)
self.arbor_sim: arbor.simulation = None


Expand Down Expand Up @@ -375,11 +375,13 @@ def __init__(self, comm=None):
super().__init__(comm)
self.simdata: dict[ArborSimulation, ArborSimulationData] = {}

def prepare(self, simulation: "ArborSimulation") -> ArborSimulationData:
def prepare(
self, simulation: "ArborSimulation", filename=None
) -> ArborSimulationData:
"""
Prepares the arbor simulation engine with the given simulation.
"""
simdata = self._create_simdata(simulation)
simdata = self._create_simdata(simulation, filename)
try:
context = arbor.context(arbor.proc_allocation(threads=simulation.threads))
if self.comm.get_size() > 1:
Expand Down Expand Up @@ -466,8 +468,8 @@ def get_recipe(self, simulation, simdata=None):
self._cache_devices(simulation, simdata)
return ArborRecipe(simulation, simdata)

def _create_simdata(self, simulation):
self.simdata[simulation] = simdata = ArborSimulationData(simulation)
def _create_simdata(self, simulation, filename):
self.simdata[simulation] = simdata = ArborSimulationData(simulation, filename)
self._assign_chunks(simulation, simdata)
return simdata

Expand Down
4 changes: 1 addition & 3 deletions packages/bsb-core/bsb/cli/commands/_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,11 @@ def handler(self, context):
level=0,
)
try:
result = network.run_simulation(sim_name)
network.run_simulation(sim_name, output_filename=root / f"{uuid4()}.nio")
except NodeNotFoundError as e:
append = ", " if len(network.simulations) else ""
append += ", ".join(f"'{name}'" for name in extra_simulations)
errr.wrap(type(e), e, append=append)
else:
result.write(root / f"{uuid4()}.nio", "ow")

def get_options(self):
return {
Expand Down
4 changes: 2 additions & 2 deletions packages/bsb-core/bsb/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def run_pipelines(self, fail_fast=True, pipelines=None):
pool.schedule(pipelines)
pool.execute()

def run_simulation(self, simulation_name: str):
def run_simulation(self, simulation_name: str, output_filename: str = None):
"""
Run a simulation starting from the default single-instance adapter.

Expand All @@ -460,7 +460,7 @@ def run_simulation(self, simulation_name: str):
adapter = get_simulation_adapter(
simulation.simulator, comm=self._comm.get_communicator()
)
return adapter.simulate(simulation)[0]
return adapter.simulate(simulation, filename=output_filename)[0]

def get_simulation(self, sim_name: str) -> Simulation:
"""
Expand Down
10 changes: 5 additions & 5 deletions packages/bsb-core/bsb/simulation/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def use_bar(self):


class SimulationData:
def __init__(self, simulation: "Simulation", result=None):
def __init__(self, simulation: "Simulation", result=None, filename=None):
self.chunks = None
self.populations = dict()
self.placement: dict[CellModel, PlacementSet] = {
Expand All @@ -75,7 +75,7 @@ def __init__(self, simulation: "Simulation", result=None):
self.connections = dict()
self.devices = dict()
if result is None:
result = SimulationResult(simulation)
result = SimulationResult(simulation, filename=filename)
self.result: SimulationResult = result


Expand All @@ -92,7 +92,7 @@ def __init__(self, comm=None):
self._duration = None
self.current_checkpoint = 0

def simulate(self, *simulations, post_prepare=None):
def simulate(self, *simulations, post_prepare=None, filename=None):
"""
Simulate the given simulations.

Expand All @@ -113,7 +113,7 @@ def simulate(self, *simulations, post_prepare=None):
self._controllers.append(listener)

for simulation in simulations:
data = self.prepare(simulation)
data = self.prepare(simulation, filename)
alldata.append(data)
for hook in simulation.post_prepare:
hook(self, simulation, data)
Expand All @@ -123,7 +123,7 @@ def simulate(self, *simulations, post_prepare=None):
return self.collect(results)

@abc.abstractmethod
def prepare(self, simulation): # pragma: nocover
def prepare(self, simulation, filename=None): # pragma: nocover
"""
Reset the simulation backend and prepare for the given simulation.

Expand Down
47 changes: 37 additions & 10 deletions packages/bsb-core/bsb/simulation/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,41 @@


class SimulationResult:
def __init__(self, simulation):
from neo import Block
def __init__(self, simulation, filename=None):
from neo import Block, io

tree = simulation.__tree__()
with contextlib.suppress(KeyError):
del tree["post_prepare"]
self.block = Block(name=simulation.name, config=tree)
if filename:
self.filename = filename
self.name = simulation.name
io = io.NixIO(filename, mode="rw")
io.write(Block(name=self.name, nix_name=self.name, config=tree))
for i, nixblock in enumerate(io.nix_file.blocks):
Comment thread
filimarc marked this conversation as resolved.
if self.name == nixblock.name:
self.block_id = i
io.close()
else:
self.block = Block(
name=simulation.name, nix_name=simulation.name, config=tree
)

self.recorders = []

@property
def spiketrains(self):
return self.block.segments[0].spiketrains
def analogsignals(self):
if hasattr(self, "block"):
Comment thread
filimarc marked this conversation as resolved.
return self.block.segments[0].analogsignals
else:
return []

@property
def analogsignals(self):
return self.block.segments[0].analogsignals
def spiketrains(self):
if hasattr(self, "block"):
return self.block.segments[0].spiketrains
else:
return []

def add(self, recorder):
self.recorders.append(recorder)
Expand All @@ -36,21 +55,29 @@ def create_recorder(self, flush: typing.Callable[["neo.core.Segment"], None]):
return recorder

def flush(self):
from neo import Segment
from neo import Segment, io

segment = Segment()
self.block.segments.append(segment)
for recorder in self.recorders:
try:
recorder.flush(segment)
except Exception:
traceback.print_exc()
warn("Recorder errored out!")
if hasattr(self, "filename"):
out_stream = io.NixIO(self.filename, mode="rw")
block = out_stream.nix_file.blocks[self.block_id]
out_stream._write_segment(segment, block)
out_stream.close()
del segment
else:
self.block.segments.append(segment)

def write(self, filename, mode):
from neo import io

io.NixIO(filename, mode=mode).write(self.block)
if hasattr(self, "block"):
io.NixIO(filename, mode=mode).write(self.block)


class SimulationRecorder:
Expand Down
Loading
Loading