Source code for bluecellulab.circuit.config.bluepy_simulation_config
# 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.
from __future__ import annotations
from typing import Any, Optional
from bluecellulab.exceptions import ExtraDependencyMissingError
from bluecellulab import BLUEPY_AVAILABLE
from functools import lru_cache
from pathlib import Path
if BLUEPY_AVAILABLE:
from bluepy_configfile.configfile import BlueConfig
from bluepy.utils import open_utf8
from bluecellulab.circuit.config.sections import Conditions, ConnectionOverrides
from bluecellulab.stimulus.circuit_stimulus_definitions import Stimulus
[docs]
class BluepySimulationConfig:
"""Bluepy implementation of SimulationConfig protocol."""
_connection_overrides: list[ConnectionOverrides] = []
def __init__(self, config: str) -> None:
"""A str or a BlueConfig object are valid."""
if not BLUEPY_AVAILABLE:
raise ExtraDependencyMissingError("bluepy")
if isinstance(config, str):
if not Path(config).exists():
raise FileNotFoundError(f"Config file {config} not found.")
else:
with open_utf8(config) as f:
self.impl = BlueConfig(f)
else:
self.impl = config
def get_all_projection_names(self) -> list[str]:
unique_names = {proj.name for proj in self.impl.typed_sections('Projection')}
return list(unique_names)
[docs]
def get_all_stimuli_entries(self) -> list[Stimulus]:
"""Get all stimuli entries."""
result = []
for entry in self.impl.typed_sections('StimulusInject'):
# retrieve the stimulus to apply
stimulus_name: str = entry.Stimulus
# bluepy magic to add underscore Stimulus underscore
# stimulus_name
stimulus = self.impl['Stimulus_%s' % stimulus_name].to_dict()
stimulus["Target"] = entry.Target
stimulus = Stimulus.from_blueconfig(stimulus)
if stimulus:
result.append(stimulus)
return result
[docs]
@lru_cache(maxsize=1)
def condition_parameters(self) -> Conditions:
"""Returns parameters of global condition block of the blueconfig."""
try:
condition_entries = self.impl.typed_sections('Conditions')[0].to_dict()
except IndexError:
return Conditions.init_empty()
return Conditions.from_blueconfig(condition_entries)
@lru_cache(maxsize=1)
def _connection_entries(self) -> list[ConnectionOverrides]:
result = []
for conn_entry in self.impl.typed_sections('Connection'):
# SynapseID is ignored by bluepy thus not part of dict representation
conn_entry_dict = conn_entry.to_dict()
if "SynapseID" in conn_entry:
conn_entry_dict["SynapseID"] = int(conn_entry["SynapseID"])
result.append(ConnectionOverrides.from_blueconfig(conn_entry_dict))
return result
def connection_entries(self) -> list[ConnectionOverrides]:
return self._connection_entries() + self._connection_overrides
@property
def base_seed(self) -> int:
"""Base seed of blueconfig."""
return int(self.impl.Run['BaseSeed']) if 'BaseSeed' in self.impl.Run else 0
@property
def synapse_seed(self) -> int:
"""Synapse seed of blueconfig."""
return int(self.impl.Run['SynapseSeed']) if 'SynapseSeed' in self.impl.Run else 0
@property
def ionchannel_seed(self) -> int:
"""Ion channel seed of blueconfig."""
return int(self.impl.Run['IonChannelSeed']) if 'IonChannelSeed' in self.impl.Run else 0
@property
def stimulus_seed(self) -> int:
"""Stimulus seed of blueconfig."""
return int(self.impl.Run['StimulusSeed']) if 'StimulusSeed' in self.impl.Run else 0
@property
def minis_seed(self) -> int:
"""Minis seed of blueconfig."""
return int(self.impl.Run['MinisSeed']) if 'MinisSeed' in self.impl.Run else 0
@property
def rng_mode(self) -> str:
"""Gets the rng mode defined in simulation."""
# Ugly, but mimicking neurodamus
if 'Simulator' in self.impl.Run and self.impl.Run['Simulator'] != 'NEURON':
return 'Random123'
elif 'RNGMode' in self.impl.Run:
return self.impl.Run['RNGMode']
else:
return "Compatibility" # default rng mode
@property
def spike_threshold(self) -> float:
"""Get the spike threshold from simulation config."""
if 'SpikeThreshold' in self.impl.Run:
return float(self.impl.Run['SpikeThreshold'])
else:
return -30.0
@property
def spike_location(self) -> str:
"""Get the spike location from simulation config."""
if 'SpikeLocation' in self.impl.Run:
return self.impl.Run['SpikeLocation']
else:
return "soma"
@property
def duration(self) -> Optional[float]:
"""Get the duration of the simulation."""
if 'Duration' in self.impl.Run:
return float(self.impl.Run['Duration'])
else:
return None
@property
def dt(self) -> float:
return float(self.impl.Run['Dt'])
@property
def _soma_report_dt(self) -> float:
return float(self.impl.Report_soma['Dt'])
@property
def forward_skip(self) -> Optional[float]:
if 'ForwardSkip' in self.impl.Run:
return float(self.impl.Run['ForwardSkip'])
return None
@property
def celsius(self) -> float:
if 'Celsius' in self.impl.Run:
return float(self.impl.Run['Celsius'])
else:
return 34.0 # default
@property
def v_init(self) -> float:
if 'V_Init' in self.impl.Run:
return float(self.impl.Run['V_Init'])
else:
return -65.0
@property
def output_root_path(self) -> str:
"""Get the output root path."""
return self.impl.Run['OutputRoot']
@property
def extracellular_calcium(self) -> Optional[float]:
"""Get the extracellular calcium value."""
if 'ExtracellularCalcium' in self.impl.Run:
return float(self.impl.Run['ExtracellularCalcium'])
else:
return None
@property
def tstart(self) -> Optional[float]:
return 0.0
@property
def tstop(self) -> Optional[float]:
if 'Duration' in self.impl.Run:
return float(self.impl.Run['Duration'])
return None
def add_connection_override(
self,
connection_override: ConnectionOverrides
) -> None:
self._connection_overrides.append(connection_override)
[docs]
def get_modifications(self) -> list:
"""Bluepy configs do not support modifications."""
return []
[docs]
def get_compartment_sets(self) -> dict[str, dict[str, Any]]:
"""Bluepy configs do not support compartment_sets."""
raise NotImplementedError("Compartment sets are only supported for SonataSimulationConfig.")