Source code for bluecellulab.simulation.simulation
# Copyright 2023-2024 Blue Brain Project / EPFL
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Simulation class of bluecellulab."""
import contextlib
import sys
from typing import Optional
import logging
import neuron
import bluecellulab
logger = logging.getLogger(__name__)
[docs]
class Simulation:
"""Class that represents a neuron simulation."""
def __init__(self, parallel_context=None, custom_progress_function=None) -> None:
self.cells: list[bluecellulab.Cell] = []
self.fih_progress = None
self.progress = None
self.progress_closed = None
self.progress_dt: Optional[float] = None
self.pc = parallel_context
self.custom_progress_function = custom_progress_function
[docs]
def add_cell(self, new_cell: bluecellulab.Cell) -> None:
"""Add a cell to a simulation."""
self.cells.append(new_cell)
[docs]
def init_progress_callback(self):
"""Initiziale the progress bar callback."""
self.progress = 0
if not self.fih_progress:
self.fih_progress = neuron.h.FInitializeHandler(
1, self.progress_callback)
self.progress_closed = False
[docs]
def progress_callback(self):
"""Callback function for the progress bar."""
if self.custom_progress_function is None:
if self.progress > 0:
sys.stdout.write("\x1b[3F")
self.progress += 1
self.progress_closed = not self.progress_closed
if self.progress_closed:
sys.stdout.write(" %s%s%s \n" % (" " * (
self.progress - 1), " ", " " * (100 - self.progress)))
sys.stdout.write("[%s%s%s]\n" % ("#" * (
self.progress - 1), "-", "." * (100 - self.progress)))
sys.stdout.write(" %s%s%s \n" % (" " * (
self.progress - 1), " ", " " * (100 - self.progress)))
else:
sys.stdout.write(" %s%s%s \n" % (" " * (
self.progress - 1), "/", " " * (100 - self.progress)))
sys.stdout.write("[%s%s%s]\n" % ("#" * (
self.progress - 1), ">", "." * (100 - self.progress)))
sys.stdout.write(" %s%s%s \n" % (" " * (
self.progress - 1), "\\", " " * (100 - self.progress)))
sys.stdout.flush()
else:
self.custom_progress_function()
neuron.h.cvode.event(
neuron.h.t + self.progress_dt, self.progress_callback)
[docs]
def init_callbacks(self):
"""Initialize the callback of all the registered simulation objects.
(e.g. for window plotting)
"""
for cell in self.cells:
cell.init_callbacks()
[docs]
def run(
self,
tstop: float,
cvode=True,
cvode_minstep=None,
cvode_maxstep=None,
dt=0.025,
forward_skip=None,
forward_skip_value=False,
show_progress=None
):
"""Run the simulation."""
# if maxtime <= neuron.h.t:
# raise Exception("Simulation: need to provide a maxtime (=%f) "
# "that is bigger than the current time (=%f)" % (
# maxtime, neuron.h.t))
if show_progress is None:
show_progress = bluecellulab.VERBOSE_LEVEL > 1
if show_progress:
self.progress_dt = tstop / 100
self.init_progress_callback()
neuron.h.tstop = tstop
cvode_old_status = neuron.h.cvode_active()
if cvode:
neuron.h.cvode_active(1)
if cvode_minstep:
neuron.h.cvode.minstep(cvode_minstep)
if cvode_maxstep:
neuron.h.cvode.maxstep(cvode_maxstep)
else:
if cvode_old_status:
logger.warning("cvode was activated outside of Simulation, "
"temporarily disabling it in run() because cvode=False "
"was set")
neuron.h.cvode_active(0)
for cell in self.cells:
with contextlib.suppress(AttributeError):
cell.re_init_rng()
neuron.h.dt = dt
neuron.h.steps_per_ms = 1.0 / dt
# WARNING: Be 'very' careful when you change something below.
# This can easily break the BGLib replay, since the way things are
# initialized heavily influence the random number generator
# e.g. finitialize() + step() != run()
self.init_callbacks()
logger.debug(f'Running a simulation until {tstop} ms ...')
neuron.h.stdinit()
if forward_skip:
if forward_skip_value is not None and forward_skip_value > 0:
neuron.h.t = -1e9
save_dt = neuron.h.dt
neuron.h.dt = forward_skip_value * 0.1
for _ in range(0, 10):
neuron.h.fadvance()
neuron.h.dt = save_dt
neuron.h.t = forward_skip_value
try:
neuron.h.continuerun(neuron.h.tstop)
except Exception as exception:
stream_handler = logging.StreamHandler(sys.stderr)
logger.addHandler(stream_handler)
logger.error("The neuron was eaten by the Python !\n"
"Reason: % s: % s" % (exception.__class__.__name__, exception))
logger.removeHandler(stream_handler)
finally:
if cvode_old_status:
logger.warning(
"cvode was activated outside of Simulation, "
"this might make it impossible to load templates with "
"stochastic channels")
neuron.h.cvode_active(cvode_old_status)
logger.debug("Finished simulation.")