Skip to content

Commit 8e86f60

Browse files
Kruthi MunnangiKruthi Munnangi
authored andcommitted
Fix default parameters and safe name lookups for light-dependent sensors
1 parent 8d0f370 commit 8e86f60

2 files changed

Lines changed: 34 additions & 23 deletions

File tree

‎cleo/imaging/sensors.py‎

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
class Sensor(SynapseDevice):
1212
"""Base class for sensors"""
1313

14+
name: str = field(default="sensor", kw_only=True)
15+
"""name of the sensor device"""
1416
sigma_noise: float = field(kw_only=True)
1517
"""standard deviation of Gaussian noise in ΔF/F measurement"""
1618
dFF_1AP: float = field(kw_only=True)
@@ -73,15 +75,15 @@ class DynamicCalcium(CalciumModel):
7375
)
7476
"""from eq 8 in Lütke et al., 2013"""
7577

76-
Ca_rest: Quantity = field(kw_only=True)
78+
Ca_rest: Quantity = field(default=50 * nmolar, kw_only=True)
7779
"""resting Ca2+ concentration (molar)"""
78-
gamma: Quantity = field(kw_only=True)
80+
gamma: Quantity = field(default=292.3 / second, kw_only=True)
7981
"""clearance/extrusion rate (1/sec)"""
80-
B_T: Quantity = field(kw_only=True)
82+
B_T: Quantity = field(default=200 * umolar, kw_only=True)
8183
"""total indicator (buffer) concentration (molar)"""
82-
kappa_S: float = field(kw_only=True)
84+
kappa_S: float = field(default=110.0, kw_only=True)
8385
"""Ca2+ binding ratio of the endogenous buffer"""
84-
dCa_T: Quantity = field(kw_only=True)
86+
dCa_T: Quantity = field(default=7.6 * umolar, kw_only=True)
8587
"""total Ca2+ concentration increase per spike (molar)"""
8688

8789
def init_syn_vars(self, syn: Synapses) -> None:
@@ -95,7 +97,6 @@ class CalBindingActivationModel:
9597
model: str
9698

9799

98-
99100
@define(eq=False)
100101
class NullBindingActivation(CalBindingActivationModel):
101102
"""Doesn't model binding/activation; i.e., goes straight from [Ca2+] to ΔF/F"""
@@ -168,11 +169,11 @@ class LightExcitation(ExcitationModel):
168169
* exp(-k * Irr * soma_radius**2 * pi * (mwatt)) * (1/Hz)) : 1
169170
""", init=False)
170171

171-
baseline: float = field(kw_only=True)
172-
A: float = field(kw_only=True)
173-
n: float = field(kw_only=True)
174-
ec50: float = field(kw_only=True)
175-
k: float = field(kw_only=True)
172+
baseline: float = field(default=1.0, kw_only=True)
173+
A: float = field(default=1.0, kw_only=True)
174+
n: float = field(default=1.0, kw_only=True)
175+
ec50: float = field(default=1.0, kw_only=True)
176+
k: float = field(default=0.0, kw_only=True)
176177

177178
@define(eq=False)
178179
class GECI(Sensor):
@@ -186,6 +187,7 @@ class GECI(Sensor):
186187
to data, rather than to biophysical processes before the data.
187188
"""
188189

190+
name: str = field(default="GECI", kw_only=True)
189191
cal_model: CalciumModel = field(kw_only=True)
190192
bind_act_model: CalBindingActivationModel = field(kw_only=True)
191193
exc_model: ExcitationModel = field(kw_only=True)
@@ -251,9 +253,15 @@ def params(self) -> dict:
251253

252254
@define(eq=False, slots=False)
253255
class LightDependentGECI(GECI, LightDependent):
254-
"""Light-dependent calcium indicator (not yet implemented)"""
255-
"""Uses a Hill equation to convert from Ca2+ to ΔF/F, as in Song et al., 2021"""
256-
pass
256+
"""Light-dependent calcium indicator"""
257+
258+
name: str = field(default="LightDependentGECI", kw_only=True)
259+
260+
def __attrs_post_init__(self):
261+
super().__attrs_post_init__()
262+
# Ensure LightDependent mixin initialization runs to set spectrum defaults
263+
if hasattr(LightDependent, "__post_init__"):
264+
LightDependent.__post_init__(self)
257265

258266
def geci(
259267
light_dependent: bool, doub_exp_conv: bool, pre_existing_cal: bool, **kwparams
@@ -402,8 +410,6 @@ def geci_fn(
402410
**extra_kwparams,
403411
)
404412

405-
# geci_fn.spectrum = spectrum
406-
407413
geci_fn.__doc__ += "\n\n" + (" " * 8) + extra_doc
408414

409415
globals()[brian_safe_name(name.lower())] = geci_fn
@@ -448,6 +454,7 @@ def load_geci_dataframe(name: str):
448454
return gcamp
449455
except Exception as e:
450456
print(f"Failed to register sensors from csv: {e}")
457+
451458
import matplotlib.pyplot as plt
452459
def Plot_Generator(time_history, dFF_history, count):
453460
for i in range(len(dFF_history[0])):

‎cleo/light/light_dependence.py‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ class LightDependent:
7878

7979
@spectrum.default
8080
def _default_spectrum(self):
81+
device_name = getattr(self, "name", self.__class__.__name__)
8182
warnings.warn(
82-
f"No spectrum provided for light-dependent device {self.name}."
83+
f"No spectrum provided for light-dependent device {device_name}."
8384
" Assuming ε = 1 for all λ."
8485
)
8586
return [(-1e10, 1), (1e10, 1)]
@@ -101,14 +102,15 @@ def light_agg_ngs(self) -> dict[str, NeuronGroup]:
101102
def _get_source_for_synapse(
102103
self, target_ng: NeuronGroup, i_targets: list[int]
103104
) -> Tuple[NeuronGroup, list[int]]:
105+
device_name = getattr(self, "name", self.__class__.__name__)
104106
# create light aggregator neurons
105107
light_agg_ng = NeuronGroup(
106108
len(i_targets),
107109
model="""
108110
phi : 1/second/meter**2
109111
Irr : watt/meter**2
110112
""",
111-
name=f"light_agg_{brian_safe_name(self.name)}_{target_ng.name}",
113+
name=f"light_agg_{brian_safe_name(device_name)}_{target_ng.name}",
112114
)
113115
assign_xyz(
114116
light_agg_ng,
@@ -122,6 +124,7 @@ def _get_source_for_synapse(
122124
def epsilon(self, lambda_new: Quantity) -> float:
123125
"""Returns the :math:`\\varepsilon` value for a given lambda (including units)
124126
representing the relative sensitivity of the opsin to that wavelength."""
127+
device_name = getattr(self, "name", self.__class__.__name__)
125128
lam_eps_array = np.array(self.spectrum)
126129
lambdas, epsilons = lam_eps_array[lam_eps_array[:, 0].argsort()].T
127130
lambda_new /= nmeter
@@ -132,19 +135,19 @@ def epsilon(self, lambda_new: Quantity) -> float:
132135
if not self.extrapolate:
133136
warnings.warn(
134137
f"λ = {lambda_new} nm is outside the range of the action spectrum data"
135-
f" for {self.name} and extrapolate=False. Assuming ε = 0."
138+
f" for {device_name} and extrapolate=False. Assuming ε = 0."
136139
)
137140
return 0
138141
elif self.extrapolate:
139142
warnings.warn(
140143
f"λ = {lambda_new} nm is outside the range of the action spectrum data"
141-
f" for {self.name}. Extrapolating: ε = {eps_new:.3f}."
144+
f" for {device_name}. Extrapolating: ε = {eps_new:.3f}."
142145
)
143146
if eps_new < 0:
144-
warnings.warn(f"ε = {eps_new} < 0 for {self.name}. Setting ε = 0.")
147+
warnings.warn(f"ε = {eps_new} < 0 for {device_name}. Setting ε = 0.")
145148
eps_new = 0
146149
if eps_new > 1:
147-
warnings.warn(f"ε = {eps_new} > 1 for {self.name}. Setting ε = 1.")
150+
warnings.warn(f"ε = {eps_new} > 1 for {device_name}. Setting ε = 1.")
148151
eps_new = 1
149152
return eps_new
150153

@@ -219,7 +222,8 @@ def plot_spectra(
219222
epsilons_new = ldd.spectrum_interpolator(lambdas, epsilons, lambdas_new)
220223
c_points = [wavelength_to_rgb(l) for l in lambdas * nmeter]
221224
c_line = wavelength_to_rgb(lambdas[np.argmax(epsilons)] * nmeter)
222-
ax.plot(lambdas_new, epsilons_new, c=c_line, label=ldd.name)
225+
device_name = getattr(ldd, "name", ldd.__class__.__name__)
226+
ax.plot(lambdas_new, epsilons_new, c=c_line, label=device_name)
223227
ax.scatter(lambdas, epsilons, marker="o", s=50, color=c_points)
224228

225229
title = (

0 commit comments

Comments
 (0)