"""Custom Cell class."""
# Copyright 2020-2022 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.
import logging
from bluepyopt import ephys
from emodelrunner.create_hoc_tools import create_hoc
logger = logging.getLogger(__name__)
[docs]
class CellModelCustom(ephys.models.CellModel):
"""Cell model class.
Attributes:
name (str): name of the model
morphology (bluepyopt.ephys.morphologies.Morphology):
underlying Morphology of the cell
mechanisms (list of bluepyopt.ephys.mechanisms.Mechanisms):
Mechanisms associated with the cell
params (collections.OrderedDict of bluepyopt.Parameters): Parameters of the cell model
icell (neuron cell): Cell instantiation in simulator
param_values (dict): contains values of the optimized parameters
gid (int): cell's ID
seclist_names (list of strings): Names of the lists of sections
secarray_names (list of strings): Names of the sections
add_synapses (bool): set to True to add synapses to the cell
fixhp (bool): to uninsert SK_E2 for hyperpolarization
"""
def __init__(
self,
name,
morph=None,
mechs=None,
params=None,
gid=0,
add_synapses=False,
fixhp=False,
):
"""Constructor.
Args:
name (str): name of this object
should be alphanumeric string, underscores are allowed,
first char should be a letter
morph (Morphology): underlying Morphology of the cell
mechs (list of Mechanisms): Mechanisms associated with the cell
params (list of Parameters): Parameters of the cell model
gid (int): id of cell
add_synapses (bool): set to True to add synapses to the cell
fixhp (bool): to uninsert SK_E2 for hyperpolarization
"""
super().__init__(name, morph, mechs, params, gid)
self.add_synapses = add_synapses
self.fixhp = fixhp
[docs]
def get_replace_axon(self):
"""Return appropriate replace_axon str.
Returns:
str: hoc script defining the replacement axon
"""
if self.morphology.do_replace_axon:
replace_axon = self.morphology.replace_axon_hoc
else:
replace_axon = None
if (
self.morphology.morph_modifiers is not None
and self.morphology.morph_modifiers_hoc is None
):
logger.warning(
"You have provided custom morphology modifiers, \
but no corresponding hoc files."
)
elif (
self.morphology.morph_modifiers is not None
and self.morphology.morph_modifiers_hoc is not None
):
if replace_axon is None:
replace_axon = ""
for morph_modifier_hoc in self.morphology.morph_modifiers_hoc:
replace_axon += "\n"
replace_axon += morph_modifier_hoc
return replace_axon
[docs]
def freeze_params(self, param_values):
"""Freeze params and return list of params to unfreze afterwards.
Args:
param_values (dict): contains values of the optimized parameters
Returns:
list: names of the newly frozen parameters
"""
to_unfreeze = []
for param in self.params.values():
if not param.frozen:
param.freeze(param_values[param.name])
to_unfreeze.append(param.name)
return to_unfreeze
[docs]
def remove_point_process_mechs(self):
"""Return mechanisms without point process mechanisms.
Returns:
list of Mechanisms without the point process mechanisms
"""
mechs = []
for mech in self.mechanisms:
if not hasattr(mech, "pprocesses"):
mechs.append(mech)
return mechs
[docs]
def create_custom_hoc(
self,
param_values,
ignored_globals=(),
template_path="templates/cell_template.jinja2",
disable_banner=False,
syn_dir=None,
syn_hoc_filename=None,
syn_temp_name="hoc_synapses",
):
"""Create hoc code for this model.
Args:
param_values (dict): contains values of the optimized parameters
ignored_globals (iterable str): HOC coded is added for each
NrnGlobalParameter that exists, to test that it matches the values
set in the parameters.
This iterable contains parameter names that aren't checked
template_path (str): path to the jinja2 template
disable_banner (bool): if not True: a banner is added to the hoc file
syn_dir (str): directory where the synapse data /files are
syn_hoc_filename (str): file name of synapse hoc file
syn_temp_name (str): synapse class name in hoc
Returns:
str: hoc script describing this cell model
"""
# pylint: disable=too-many-arguments
to_unfreeze = self.freeze_params(param_values)
replace_axon = self.get_replace_axon()
mechs = self.remove_point_process_mechs()
ret = create_hoc(
mechs=mechs,
parameters=self.params.values(),
ignored_globals=ignored_globals,
replace_axon=replace_axon,
template_name=self.name,
template_path=template_path,
disable_banner=disable_banner,
add_synapses=self.add_synapses,
synapses_template_name=syn_temp_name,
syn_hoc_filename=syn_hoc_filename,
syn_dir=syn_dir,
)
self.unfreeze(to_unfreeze)
return ret
[docs]
@staticmethod
def connectable_empty_cell_template(
template_name,
seclist_names=None,
secarray_names=None,
):
"""Create a hoc script of an empty cell with an additional connection function.
Args:
template_name (str): name of the cell model
seclist_names (list of strings): Names of the lists of sections
secarray_names (list of strings): Names of the sections
Returns:
str: hoc script describing an empty cell model with connection function
"""
objref_str = "objref this, CellRef"
newseclist_str = ""
if seclist_names:
for seclist_name in seclist_names:
objref_str += f", {seclist_name}"
newseclist_str += f" {seclist_name} = new SectionList()\n"
secarrays_str = ""
if secarray_names:
secarrays_str = "create "
secarrays_str += ", ".join(
f"{secarray_name}[1]" for secarray_name in secarray_names
)
secarrays_str += "\n"
template = """\
begintemplate %(template_name)s
%(objref_str)s
public init, getCell, getCCell, connect2target
public soma, dend, apic, axon, myelin
%(secarrays_str)s
obfunc getCell(){
return this
}
obfunc getCCell(){
return CellRef
}
/*!
* @param $o1 NetCon source (can be nil)
* @param $o2 Variable where generated NetCon will be placed
*/
proc connect2target() { //$o1 target point process, $o2 returned NetCon
soma $o2 = new NetCon(&v(1), $o1)
$o2.threshold = -30
}
proc init() {\n%(newseclist_str)s
forall delete_section()
CellRef = this
}
gid = 0
proc destroy() {localobj nil
CellRef = nil
}
endtemplate %(template_name)s
""" % {
"template_name": template_name,
"objref_str": objref_str,
"newseclist_str": newseclist_str,
"secarrays_str": secarrays_str,
}
return template
[docs]
@staticmethod
def create_empty_cell(name, sim, seclist_names=None, secarray_names=None):
"""Create an empty cell in Neuron.
Args:
template_name (str): name of the cell model
sim (bluepyopt.ephys.NrnSimulator): neuron simulator
seclist_names (list of strings): Names of the lists of sections
secarray_names (list of strings): Names of the sections
Returns:
Cell instantiation in simulator
"""
hoc_template = CellModelCustom.connectable_empty_cell_template(
name, seclist_names, secarray_names
)
sim.neuron.h(hoc_template)
template_function = getattr(sim.neuron.h, name)
return template_function()
[docs]
def instantiate(self, sim=None):
"""Instantiate model in simulator.
Args:
sim (bluepyopt.ephys.NrnSimulator): neuron simulator
"""
# pylint: disable=unnecessary-comprehension
super().instantiate(sim)
# Hyperpolarization workaround
somatic = [x for x in self.icell.somatic]
axonal = [x for x in self.icell.axonal]
if self.fixhp:
for sec in somatic + axonal:
sec.uninsert("SK_E2")