bmtool 0.7.6__py3-none-any.whl → 0.7.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bmtool might be problematic. Click here for more details.
- bmtool/analysis/entrainment.py +113 -0
- bmtool/analysis/lfp.py +1 -1
- bmtool/bmplot/connections.py +759 -337
- bmtool/bmplot/entrainment.py +169 -49
- bmtool/bmplot/lfp.py +146 -11
- bmtool/bmplot/netcon_reports.py +1 -0
- bmtool/bmplot/spikes.py +175 -18
- bmtool/connectors.py +386 -0
- bmtool/singlecell.py +474 -31
- bmtool/synapses.py +1684 -651
- bmtool/util/util.py +40 -5
- {bmtool-0.7.6.dist-info → bmtool-0.7.8.dist-info}/METADATA +1 -1
- {bmtool-0.7.6.dist-info → bmtool-0.7.8.dist-info}/RECORD +17 -17
- {bmtool-0.7.6.dist-info → bmtool-0.7.8.dist-info}/WHEEL +0 -0
- {bmtool-0.7.6.dist-info → bmtool-0.7.8.dist-info}/entry_points.txt +0 -0
- {bmtool-0.7.6.dist-info → bmtool-0.7.8.dist-info}/licenses/LICENSE +0 -0
- {bmtool-0.7.6.dist-info → bmtool-0.7.8.dist-info}/top_level.txt +0 -0
bmtool/singlecell.py
CHANGED
|
@@ -769,7 +769,7 @@ class FI(object):
|
|
|
769
769
|
--------
|
|
770
770
|
tuple
|
|
771
771
|
(current_amplitudes, spike_counts) where:
|
|
772
|
-
- current_amplitudes: List of current injection amplitudes (
|
|
772
|
+
- current_amplitudes: List of current injection amplitudes (pA)
|
|
773
773
|
- spike_counts: List of spike counts corresponding to each amplitude
|
|
774
774
|
"""
|
|
775
775
|
print("Running simulations for FI curve...")
|
|
@@ -781,14 +781,14 @@ class FI(object):
|
|
|
781
781
|
print()
|
|
782
782
|
print("Results")
|
|
783
783
|
# lets make a df so the results line up nice
|
|
784
|
-
data = {"Injection (
|
|
784
|
+
data = {"Injection (pA):": [amp * 1000 for amp in self.amps], "number of spikes": self.nspks}
|
|
785
785
|
df = pd.DataFrame(data)
|
|
786
786
|
print(df)
|
|
787
|
-
# print(f'Injection (
|
|
787
|
+
# print(f'Injection (pA): ' + ', '.join(f'{x:g}' for x in self.amps))
|
|
788
788
|
# print(f'Number of spikes: ' + ', '.join(f'{x:d}' for x in self.nspks))
|
|
789
789
|
print()
|
|
790
790
|
|
|
791
|
-
return self.amps, self.nspks
|
|
791
|
+
return [amp * 1000 for amp in self.amps], self.nspks
|
|
792
792
|
|
|
793
793
|
|
|
794
794
|
class ZAP(CurrentClamp):
|
|
@@ -1040,6 +1040,9 @@ class Profiler:
|
|
|
1040
1040
|
# initialize to None and then prefer config-derived paths if provided
|
|
1041
1041
|
self.template_dir = None
|
|
1042
1042
|
self.mechanism_dir = None
|
|
1043
|
+
self.templates = None # Initialize templates attribute
|
|
1044
|
+
self.config = config # Store config path
|
|
1045
|
+
self.last_figure = None # Store reference to last generated figure
|
|
1043
1046
|
|
|
1044
1047
|
# If a BMTK config is provided, load mechanisms/templates from it
|
|
1045
1048
|
if config is not None:
|
|
@@ -1067,7 +1070,7 @@ class Profiler:
|
|
|
1067
1070
|
load_templates_from_config(config)
|
|
1068
1071
|
except Exception:
|
|
1069
1072
|
# fall back to explicit dirs if config parsing/loading fails
|
|
1070
|
-
|
|
1073
|
+
print('failed')
|
|
1071
1074
|
|
|
1072
1075
|
else:
|
|
1073
1076
|
# fall back to explicit args if not set by config
|
|
@@ -1091,30 +1094,54 @@ class Profiler:
|
|
|
1091
1094
|
|
|
1092
1095
|
def load_templates(self, hoc_template_file=None):
|
|
1093
1096
|
if self.templates is None: # Can really only do this once
|
|
1094
|
-
if
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1097
|
+
# Check if we have a config file - if so, extract templates from node configs
|
|
1098
|
+
if hasattr(self, 'config') and self.config is not None:
|
|
1099
|
+
try:
|
|
1100
|
+
from bmtool.util.util import load_nodes_from_config
|
|
1101
|
+
nodes_networks = load_nodes_from_config(config=self.config)
|
|
1102
|
+
template_names = set()
|
|
1103
|
+
for nodes in nodes_networks:
|
|
1104
|
+
try:
|
|
1105
|
+
cell_template_names = nodes_networks[nodes]['model_template'].unique()
|
|
1106
|
+
# Clean up template names (remove 'hoc:' prefix if present)
|
|
1107
|
+
for template in cell_template_names:
|
|
1108
|
+
if isinstance(template, str):
|
|
1109
|
+
# Remove 'hoc:' prefix if present
|
|
1110
|
+
clean_name = template.replace('hoc:', '') if template.startswith('hoc:') else template
|
|
1111
|
+
template_names.add(clean_name)
|
|
1112
|
+
except:
|
|
1113
|
+
# If fails, means no model_templates in that network
|
|
1114
|
+
pass
|
|
1115
|
+
|
|
1116
|
+
self.templates = sorted(list(template_names))
|
|
1117
|
+
self.hoc_templates = [] # Templates loaded via config, not hoc files
|
|
1118
|
+
|
|
1119
|
+
except Exception as e:
|
|
1120
|
+
print(f"Failed to load templates from config: {e}")
|
|
1108
1121
|
else:
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1122
|
+
# Traditional loading with template_dir and mechanism_dir
|
|
1123
|
+
if (
|
|
1124
|
+
self.mechanism_dir != "./"
|
|
1125
|
+
and self.mechanism_dir != "."
|
|
1126
|
+
and self.mechanism_dir != "././"
|
|
1127
|
+
):
|
|
1128
|
+
neuron.load_mechanisms(self.mechanism_dir)
|
|
1129
|
+
h_base = set(dir(h))
|
|
1130
|
+
|
|
1131
|
+
cwd = os.getcwd()
|
|
1132
|
+
os.chdir(self.template_dir)
|
|
1133
|
+
if not hoc_template_file:
|
|
1134
|
+
self.hoc_templates = glob.glob("*.hoc")
|
|
1135
|
+
for hoc_template in self.hoc_templates:
|
|
1136
|
+
h.load_file(str(hoc_template))
|
|
1137
|
+
else:
|
|
1138
|
+
self.hoc_templates = [hoc_template_file]
|
|
1139
|
+
h.load_file(hoc_template_file)
|
|
1140
|
+
|
|
1141
|
+
os.chdir(cwd)
|
|
1142
|
+
|
|
1143
|
+
h_loaded = dir(h)
|
|
1144
|
+
self.templates = [x for x in h_loaded if x not in h_base]
|
|
1118
1145
|
|
|
1119
1146
|
return self.templates
|
|
1120
1147
|
|
|
@@ -1163,13 +1190,20 @@ class Profiler:
|
|
|
1163
1190
|
|
|
1164
1191
|
if plot:
|
|
1165
1192
|
plt.figure()
|
|
1166
|
-
|
|
1193
|
+
t_array = np.array(time)
|
|
1194
|
+
amp_array = np.array(amp)
|
|
1195
|
+
t_idx = (t_array >= passive.inj_delay) & (t_array <= passive.inj_delay + passive.inj_dur)
|
|
1196
|
+
plt.plot(t_array[t_idx], amp_array[t_idx])
|
|
1167
1197
|
if passive.method == "exp2":
|
|
1168
1198
|
plt.plot(*passive.double_exponential_fit(), "r:", label="double exponential fit")
|
|
1169
1199
|
plt.legend()
|
|
1200
|
+
elif passive.method == "exp":
|
|
1201
|
+
plt.plot(*passive.single_exponential_fit(), "r:", label="single exponential fit")
|
|
1202
|
+
plt.legend()
|
|
1170
1203
|
plt.title("Passive Cell Current Injection")
|
|
1171
1204
|
plt.xlabel("Time (ms)")
|
|
1172
1205
|
plt.ylabel("Membrane Potential (mV)")
|
|
1206
|
+
self.last_figure = plt.gcf()
|
|
1173
1207
|
plt.show()
|
|
1174
1208
|
|
|
1175
1209
|
return time, amp
|
|
@@ -1198,6 +1232,8 @@ class Profiler:
|
|
|
1198
1232
|
plt.title("Current Injection")
|
|
1199
1233
|
plt.xlabel("Time (ms)")
|
|
1200
1234
|
plt.ylabel("Membrane Potential (mV)")
|
|
1235
|
+
plt.xlim(ccl.inj_delay - 10, ccl.inj_delay + ccl.inj_dur + 10)
|
|
1236
|
+
self.last_figure = plt.gcf()
|
|
1201
1237
|
plt.show()
|
|
1202
1238
|
|
|
1203
1239
|
return time, amp
|
|
@@ -1228,7 +1264,7 @@ class Profiler:
|
|
|
1228
1264
|
plot: bool
|
|
1229
1265
|
automatically plot an fi curve
|
|
1230
1266
|
|
|
1231
|
-
Returns the injection amplitudes (
|
|
1267
|
+
Returns the injection amplitudes (pA) used, number of spikes per amplitude supplied
|
|
1232
1268
|
list(amps), list(# of spikes)
|
|
1233
1269
|
"""
|
|
1234
1270
|
fi = FI(
|
|
@@ -1244,8 +1280,9 @@ class Profiler:
|
|
|
1244
1280
|
plt.figure()
|
|
1245
1281
|
plt.plot(amp, nspk)
|
|
1246
1282
|
plt.title("FI Curve")
|
|
1247
|
-
plt.xlabel("Injection (
|
|
1283
|
+
plt.xlabel("Injection (pA)")
|
|
1248
1284
|
plt.ylabel("# Spikes")
|
|
1285
|
+
self.last_figure = plt.gcf()
|
|
1249
1286
|
plt.show()
|
|
1250
1287
|
|
|
1251
1288
|
return amp, nspk
|
|
@@ -1285,22 +1322,428 @@ class Profiler:
|
|
|
1285
1322
|
plt.title("ZAP Response")
|
|
1286
1323
|
plt.xlabel("Time (ms)")
|
|
1287
1324
|
plt.ylabel("Membrane Potential (mV)")
|
|
1325
|
+
self.last_figure = plt.gcf()
|
|
1288
1326
|
|
|
1289
1327
|
plt.figure()
|
|
1290
1328
|
plt.plot(time, zap.zap_vec)
|
|
1291
1329
|
plt.title("ZAP Current")
|
|
1292
1330
|
plt.xlabel("Time (ms)")
|
|
1293
1331
|
plt.ylabel("Current Injection (nA)")
|
|
1332
|
+
# Note: This will overwrite last_figure with the current plot
|
|
1333
|
+
self.last_figure = plt.gcf()
|
|
1294
1334
|
|
|
1295
1335
|
plt.figure()
|
|
1296
1336
|
plt.plot(*zap.get_impedance(smooth=smooth))
|
|
1297
1337
|
plt.title("Impedance Amplitude Profile")
|
|
1298
1338
|
plt.xlabel("Frequency (Hz)")
|
|
1299
1339
|
plt.ylabel("Impedance (MOhms)")
|
|
1340
|
+
self.last_figure = plt.gcf()
|
|
1300
1341
|
plt.show()
|
|
1301
1342
|
|
|
1302
1343
|
return time, amp
|
|
1303
1344
|
|
|
1345
|
+
def interactive_runner(self):
|
|
1346
|
+
"""Interactive runner for single cell profiling with GUI widgets.
|
|
1347
|
+
|
|
1348
|
+
This method creates an interactive interface using ipywidgets that allows
|
|
1349
|
+
users to select templates and analysis methods, adjust parameters, and run
|
|
1350
|
+
simulations with real-time plotting.
|
|
1351
|
+
"""
|
|
1352
|
+
try:
|
|
1353
|
+
import ipywidgets as widgets
|
|
1354
|
+
from IPython.display import display, clear_output
|
|
1355
|
+
import matplotlib.pyplot as plt
|
|
1356
|
+
except ImportError:
|
|
1357
|
+
raise ImportError("ipywidgets and matplotlib are required for interactive mode. Install with: pip install ipywidgets matplotlib")
|
|
1358
|
+
|
|
1359
|
+
# Get available templates
|
|
1360
|
+
available_templates = self.load_templates()
|
|
1361
|
+
|
|
1362
|
+
# Check what NEURON objects are available
|
|
1363
|
+
import neuron
|
|
1364
|
+
h = neuron.h
|
|
1365
|
+
|
|
1366
|
+
# Create widgets
|
|
1367
|
+
template_dropdown = widgets.Dropdown(
|
|
1368
|
+
options=available_templates,
|
|
1369
|
+
value=available_templates[0] if available_templates else None,
|
|
1370
|
+
description='Template:',
|
|
1371
|
+
style={'description_width': '80px'},
|
|
1372
|
+
layout=widgets.Layout(width='300px')
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
method_dropdown = widgets.Dropdown(
|
|
1376
|
+
options=['passive_properties', 'current_injection', 'fi_curve', 'impedance_amplitude_profile'],
|
|
1377
|
+
value='passive_properties',
|
|
1378
|
+
description='Method:',
|
|
1379
|
+
style={'description_width': '80px'},
|
|
1380
|
+
layout=widgets.Layout(width='300px')
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
# Default values based on method - from basic_settings in single_cell_tuning.ipynb
|
|
1384
|
+
method_defaults = {
|
|
1385
|
+
'passive_properties': {
|
|
1386
|
+
'inj_amp': -20.0,
|
|
1387
|
+
'inj_delay': 1500.0,
|
|
1388
|
+
'inj_dur': 1000.0,
|
|
1389
|
+
'tstop': 2500.0,
|
|
1390
|
+
'tau_method': 'exp2'
|
|
1391
|
+
},
|
|
1392
|
+
'current_injection': {
|
|
1393
|
+
'inj_amp': 50.0,
|
|
1394
|
+
'inj_delay': 1500.0,
|
|
1395
|
+
'inj_dur': 1000.0,
|
|
1396
|
+
'tstop': 3000.0
|
|
1397
|
+
},
|
|
1398
|
+
'fi_curve': {
|
|
1399
|
+
'i_start': -100.0,
|
|
1400
|
+
'i_stop': 800.0,
|
|
1401
|
+
'i_increment': 20.0,
|
|
1402
|
+
'inj_delay': 1500.0,
|
|
1403
|
+
'inj_dur': 1000.0
|
|
1404
|
+
},
|
|
1405
|
+
'impedance_amplitude_profile': {
|
|
1406
|
+
'inj_amp': 100.0,
|
|
1407
|
+
'inj_delay': 1000.0,
|
|
1408
|
+
'inj_dur': 15000.0,
|
|
1409
|
+
'tstop': 15500.0,
|
|
1410
|
+
'fstart': 0.0,
|
|
1411
|
+
'fend': 15.0,
|
|
1412
|
+
'chirp_type': 'linear'
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
# Common parameters - always plot results, no need for toggle
|
|
1417
|
+
|
|
1418
|
+
# Method-specific parameters - styled like synapses.py sliders
|
|
1419
|
+
slider_style = {'description_width': 'initial'}
|
|
1420
|
+
slider_layout = None # Use default width for longer sliders
|
|
1421
|
+
text_style = {'description_width': 'initial'}
|
|
1422
|
+
text_layout = widgets.Layout(width='200px')
|
|
1423
|
+
|
|
1424
|
+
# Initialize sliders with default values for passive_properties (initial method)
|
|
1425
|
+
defaults = method_defaults['passive_properties']
|
|
1426
|
+
|
|
1427
|
+
inj_amp_slider = widgets.FloatSlider(value=defaults['inj_amp'], min=-500.0, max=1000.0, step=10.0, description='Injection Amp (pA):', style=slider_style)
|
|
1428
|
+
inj_delay_slider = widgets.FloatSlider(value=defaults['inj_delay'], min=0.0, max=3000.0, step=10.0, description='Injection Delay (ms):', style=slider_style)
|
|
1429
|
+
inj_dur_slider = widgets.FloatSlider(value=defaults['inj_dur'], min=100.0, max=20000.0, step=100.0, description='Injection Duration (ms):', style=slider_style)
|
|
1430
|
+
tstop_slider = widgets.FloatSlider(value=defaults['tstop'], min=500.0, max=25000.0, step=100.0, description='Total Time (ms):', style=slider_style)
|
|
1431
|
+
|
|
1432
|
+
# FI curve specific
|
|
1433
|
+
fi_defaults = method_defaults['fi_curve']
|
|
1434
|
+
i_start_slider = widgets.FloatSlider(value=fi_defaults['i_start'], min=-500.0, max=500.0, step=10.0, description='I Start (pA):', style=slider_style)
|
|
1435
|
+
i_stop_slider = widgets.FloatSlider(value=fi_defaults['i_stop'], min=0.0, max=2000.0, step=50.0, description='I Stop (pA):', style=slider_style)
|
|
1436
|
+
i_increment_slider = widgets.FloatSlider(value=fi_defaults['i_increment'], min=10.0, max=500.0, step=10.0, description='I Increment (pA):', style=slider_style)
|
|
1437
|
+
|
|
1438
|
+
# ZAP specific
|
|
1439
|
+
zap_defaults = method_defaults['impedance_amplitude_profile']
|
|
1440
|
+
fstart_slider = widgets.FloatSlider(value=zap_defaults['fstart'], min=0.0, max=50.0, step=1.0, description='Start Freq (Hz):', style=slider_style)
|
|
1441
|
+
fend_slider = widgets.FloatSlider(value=zap_defaults['fend'], min=1.0, max=100.0, step=1.0, description='End Freq (Hz):', style=slider_style)
|
|
1442
|
+
chirp_dropdown = widgets.Dropdown(options=['linear', 'exponential'], value=zap_defaults['chirp_type'], description='Chirp Type:', style=slider_style)
|
|
1443
|
+
|
|
1444
|
+
# Passive properties specific
|
|
1445
|
+
tau_method_dropdown = widgets.Dropdown(
|
|
1446
|
+
options=['simple', 'exp', 'exp2'],
|
|
1447
|
+
value=defaults['tau_method'],
|
|
1448
|
+
description='Tau Method:',
|
|
1449
|
+
style=slider_style
|
|
1450
|
+
)
|
|
1451
|
+
|
|
1452
|
+
# Sections
|
|
1453
|
+
record_sec_text = widgets.Text(value='soma', description='Record Section:', style=text_style, layout=text_layout)
|
|
1454
|
+
inj_sec_text = widgets.Text(value='soma', description='Injection Section:', style=text_style, layout=text_layout)
|
|
1455
|
+
|
|
1456
|
+
# Post init function
|
|
1457
|
+
post_init_text = widgets.Text(value='', description='Post Init Function:', placeholder='e.g., insert_mechs(123)', style={'description_width': 'initial'}, layout=widgets.Layout(width='300px'))
|
|
1458
|
+
|
|
1459
|
+
run_button = widgets.Button(
|
|
1460
|
+
description='Run Analysis',
|
|
1461
|
+
button_style='primary',
|
|
1462
|
+
icon='play',
|
|
1463
|
+
layout=widgets.Layout(width='140px')
|
|
1464
|
+
)
|
|
1465
|
+
|
|
1466
|
+
reset_button = widgets.Button(
|
|
1467
|
+
description='Reset to Defaults',
|
|
1468
|
+
button_style='warning',
|
|
1469
|
+
icon='refresh',
|
|
1470
|
+
layout=widgets.Layout(width='150px')
|
|
1471
|
+
)
|
|
1472
|
+
|
|
1473
|
+
save_path_text = widgets.Text(value='', description='Save Path:', placeholder='e.g., plot.png', style={'description_width': 'initial'}, layout=widgets.Layout(width='300px'))
|
|
1474
|
+
|
|
1475
|
+
save_button = widgets.Button(
|
|
1476
|
+
description='Save Plot',
|
|
1477
|
+
button_style='success',
|
|
1478
|
+
icon='save',
|
|
1479
|
+
layout=widgets.Layout(width='120px')
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
output_area = widgets.Output(
|
|
1483
|
+
layout=widgets.Layout(border='1px solid #ccc', padding='10px', margin='10px 0 0 0')
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1486
|
+
# Layout containers - organized like synapse tuner
|
|
1487
|
+
# Top row - template and method selection
|
|
1488
|
+
selection_row = widgets.HBox([
|
|
1489
|
+
template_dropdown,
|
|
1490
|
+
method_dropdown
|
|
1491
|
+
], layout=widgets.Layout(margin='0 0 10px 0'))
|
|
1492
|
+
|
|
1493
|
+
# Button row - main controls
|
|
1494
|
+
button_row = widgets.HBox([
|
|
1495
|
+
run_button,
|
|
1496
|
+
reset_button,
|
|
1497
|
+
save_button
|
|
1498
|
+
], layout=widgets.Layout(margin='0 0 10px 0'))
|
|
1499
|
+
|
|
1500
|
+
# Section row - recording and injection sections
|
|
1501
|
+
section_row = widgets.HBox([
|
|
1502
|
+
record_sec_text,
|
|
1503
|
+
inj_sec_text,
|
|
1504
|
+
post_init_text
|
|
1505
|
+
], layout=widgets.Layout(margin='0 0 10px 0'))
|
|
1506
|
+
|
|
1507
|
+
# Save row
|
|
1508
|
+
save_row = widgets.HBox([
|
|
1509
|
+
save_path_text
|
|
1510
|
+
], layout=widgets.Layout(margin='0 0 10px 0'))
|
|
1511
|
+
|
|
1512
|
+
# Parameter columns - organized in columns like synapse tuner
|
|
1513
|
+
injection_params_col1 = widgets.VBox([
|
|
1514
|
+
inj_amp_slider,
|
|
1515
|
+
inj_delay_slider
|
|
1516
|
+
], layout=widgets.Layout(margin='0 10px 0 0'))
|
|
1517
|
+
|
|
1518
|
+
injection_params_col2 = widgets.VBox([
|
|
1519
|
+
inj_dur_slider,
|
|
1520
|
+
tstop_slider
|
|
1521
|
+
], layout=widgets.Layout(margin='0 0 0 10px'))
|
|
1522
|
+
|
|
1523
|
+
# Passive properties specific columns
|
|
1524
|
+
passive_params_col1 = widgets.VBox([
|
|
1525
|
+
inj_amp_slider,
|
|
1526
|
+
inj_delay_slider
|
|
1527
|
+
], layout=widgets.Layout(margin='0 10px 0 0'))
|
|
1528
|
+
|
|
1529
|
+
passive_params_col2 = widgets.VBox([
|
|
1530
|
+
inj_dur_slider,
|
|
1531
|
+
tstop_slider,
|
|
1532
|
+
tau_method_dropdown
|
|
1533
|
+
], layout=widgets.Layout(margin='0 0 0 10px'))
|
|
1534
|
+
|
|
1535
|
+
fi_params_col1 = widgets.VBox([
|
|
1536
|
+
i_start_slider,
|
|
1537
|
+
i_stop_slider
|
|
1538
|
+
], layout=widgets.Layout(margin='0 10px 0 0'))
|
|
1539
|
+
|
|
1540
|
+
fi_params_col2 = widgets.VBox([
|
|
1541
|
+
i_increment_slider,
|
|
1542
|
+
inj_dur_slider # Use duration for FI curve too
|
|
1543
|
+
], layout=widgets.Layout(margin='0 0 0 10px'))
|
|
1544
|
+
|
|
1545
|
+
zap_params_col1 = widgets.VBox([
|
|
1546
|
+
inj_amp_slider,
|
|
1547
|
+
inj_delay_slider,
|
|
1548
|
+
inj_dur_slider
|
|
1549
|
+
], layout=widgets.Layout(margin='0 10px 0 0'))
|
|
1550
|
+
|
|
1551
|
+
zap_params_col2 = widgets.VBox([
|
|
1552
|
+
tstop_slider,
|
|
1553
|
+
fstart_slider,
|
|
1554
|
+
fend_slider,
|
|
1555
|
+
chirp_dropdown
|
|
1556
|
+
], layout=widgets.Layout(margin='0 0 0 10px'))
|
|
1557
|
+
|
|
1558
|
+
# Function to update slider values based on method defaults
|
|
1559
|
+
def update_slider_values(method):
|
|
1560
|
+
"""Update slider values to match the defaults for the selected method"""
|
|
1561
|
+
if method in method_defaults:
|
|
1562
|
+
defaults = method_defaults[method]
|
|
1563
|
+
|
|
1564
|
+
# Update common sliders if they exist in defaults
|
|
1565
|
+
if 'inj_amp' in defaults:
|
|
1566
|
+
inj_amp_slider.value = defaults['inj_amp']
|
|
1567
|
+
if 'inj_delay' in defaults:
|
|
1568
|
+
inj_delay_slider.value = defaults['inj_delay']
|
|
1569
|
+
if 'inj_dur' in defaults:
|
|
1570
|
+
inj_dur_slider.value = defaults['inj_dur']
|
|
1571
|
+
if 'tstop' in defaults:
|
|
1572
|
+
tstop_slider.value = defaults['tstop']
|
|
1573
|
+
|
|
1574
|
+
# Update method-specific sliders
|
|
1575
|
+
if method == 'fi_curve':
|
|
1576
|
+
if 'i_start' in defaults:
|
|
1577
|
+
i_start_slider.value = defaults['i_start']
|
|
1578
|
+
if 'i_stop' in defaults:
|
|
1579
|
+
i_stop_slider.value = defaults['i_stop']
|
|
1580
|
+
if 'i_increment' in defaults:
|
|
1581
|
+
i_increment_slider.value = defaults['i_increment']
|
|
1582
|
+
|
|
1583
|
+
elif method == 'impedance_amplitude_profile':
|
|
1584
|
+
if 'fstart' in defaults:
|
|
1585
|
+
fstart_slider.value = defaults['fstart']
|
|
1586
|
+
if 'fend' in defaults:
|
|
1587
|
+
fend_slider.value = defaults['fend']
|
|
1588
|
+
if 'chirp_type' in defaults:
|
|
1589
|
+
chirp_dropdown.value = defaults['chirp_type']
|
|
1590
|
+
|
|
1591
|
+
elif method == 'passive_properties':
|
|
1592
|
+
if 'tau_method' in defaults:
|
|
1593
|
+
tau_method_dropdown.value = defaults['tau_method']
|
|
1594
|
+
|
|
1595
|
+
|
|
1596
|
+
# Function to update parameter visibility based on selected method
|
|
1597
|
+
def update_params(*args):
|
|
1598
|
+
method = method_dropdown.value
|
|
1599
|
+
|
|
1600
|
+
# Update slider values to defaults for the selected method
|
|
1601
|
+
update_slider_values(method)
|
|
1602
|
+
|
|
1603
|
+
# Update parameter column visibility
|
|
1604
|
+
if method == 'passive_properties':
|
|
1605
|
+
param_columns.children = [widgets.HBox([passive_params_col1, passive_params_col2])]
|
|
1606
|
+
elif method == 'current_injection':
|
|
1607
|
+
param_columns.children = [widgets.HBox([injection_params_col1, injection_params_col2])]
|
|
1608
|
+
elif method == 'fi_curve':
|
|
1609
|
+
param_columns.children = [widgets.HBox([fi_params_col1, fi_params_col2])]
|
|
1610
|
+
elif method == 'impedance_amplitude_profile':
|
|
1611
|
+
param_columns.children = [widgets.HBox([zap_params_col1, zap_params_col2])]
|
|
1612
|
+
|
|
1613
|
+
method_dropdown.observe(update_params, 'value')
|
|
1614
|
+
|
|
1615
|
+
# Initialize parameter columns container
|
|
1616
|
+
param_columns = widgets.VBox([widgets.HBox([passive_params_col1, passive_params_col2])])
|
|
1617
|
+
|
|
1618
|
+
# Run function
|
|
1619
|
+
def run_analysis(b):
|
|
1620
|
+
output_area.clear_output() # Clear immediately on click
|
|
1621
|
+
with output_area:
|
|
1622
|
+
|
|
1623
|
+
template = template_dropdown.value
|
|
1624
|
+
method = method_dropdown.value
|
|
1625
|
+
record_sec = record_sec_text.value
|
|
1626
|
+
inj_sec = inj_sec_text.value
|
|
1627
|
+
post_init = post_init_text.value if post_init_text.value else None
|
|
1628
|
+
|
|
1629
|
+
kwargs = {
|
|
1630
|
+
'record_sec': record_sec,
|
|
1631
|
+
'inj_sec': inj_sec,
|
|
1632
|
+
'plot': True # Always plot results
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
if post_init:
|
|
1636
|
+
kwargs['post_init_function'] = post_init
|
|
1637
|
+
|
|
1638
|
+
# Add method-specific parameters
|
|
1639
|
+
if method == 'passive_properties':
|
|
1640
|
+
kwargs.update({
|
|
1641
|
+
'inj_amp': inj_amp_slider.value,
|
|
1642
|
+
'inj_delay': inj_delay_slider.value,
|
|
1643
|
+
'inj_dur': inj_dur_slider.value,
|
|
1644
|
+
'tstop': tstop_slider.value,
|
|
1645
|
+
'method': tau_method_dropdown.value
|
|
1646
|
+
})
|
|
1647
|
+
elif method == 'current_injection':
|
|
1648
|
+
kwargs.update({
|
|
1649
|
+
'inj_amp': inj_amp_slider.value,
|
|
1650
|
+
'inj_delay': inj_delay_slider.value,
|
|
1651
|
+
'inj_dur': inj_dur_slider.value,
|
|
1652
|
+
'tstop': tstop_slider.value
|
|
1653
|
+
})
|
|
1654
|
+
elif method == 'fi_curve':
|
|
1655
|
+
kwargs.update({
|
|
1656
|
+
'i_start': i_start_slider.value,
|
|
1657
|
+
'i_stop': i_stop_slider.value,
|
|
1658
|
+
'i_increment': i_increment_slider.value,
|
|
1659
|
+
'tstart': inj_delay_slider.value,
|
|
1660
|
+
'tdur': inj_dur_slider.value
|
|
1661
|
+
})
|
|
1662
|
+
elif method == 'impedance_amplitude_profile':
|
|
1663
|
+
kwargs.update({
|
|
1664
|
+
'inj_amp': inj_amp_slider.value,
|
|
1665
|
+
'inj_delay': inj_delay_slider.value,
|
|
1666
|
+
'inj_dur': inj_dur_slider.value,
|
|
1667
|
+
'tstop': tstop_slider.value,
|
|
1668
|
+
'fstart': fstart_slider.value,
|
|
1669
|
+
'fend': fend_slider.value,
|
|
1670
|
+
'chirp_type': chirp_dropdown.value
|
|
1671
|
+
})
|
|
1672
|
+
|
|
1673
|
+
print("="*60)
|
|
1674
|
+
print(f"Running {method} for template: {template}")
|
|
1675
|
+
print("="*60)
|
|
1676
|
+
print("Parameters:")
|
|
1677
|
+
for key, value in kwargs.items():
|
|
1678
|
+
print(f" {key}: {value}")
|
|
1679
|
+
print("-"*60)
|
|
1680
|
+
|
|
1681
|
+
try:
|
|
1682
|
+
if method == 'passive_properties':
|
|
1683
|
+
result = self.passive_properties(template, **kwargs)
|
|
1684
|
+
|
|
1685
|
+
elif method == 'current_injection':
|
|
1686
|
+
result = self.current_injection(template, **kwargs)
|
|
1687
|
+
elif method == 'fi_curve':
|
|
1688
|
+
result = self.fi_curve(template, **kwargs)
|
|
1689
|
+
elif method == 'impedance_amplitude_profile':
|
|
1690
|
+
result = self.impedance_amplitude_profile(template, **kwargs)
|
|
1691
|
+
|
|
1692
|
+
|
|
1693
|
+
except Exception as e:
|
|
1694
|
+
print("="*60)
|
|
1695
|
+
print(f"✗ Error running analysis: {e}")
|
|
1696
|
+
print("="*60)
|
|
1697
|
+
import traceback
|
|
1698
|
+
traceback.print_exc()
|
|
1699
|
+
|
|
1700
|
+
# Reset function
|
|
1701
|
+
def reset_to_defaults(b):
|
|
1702
|
+
output_area.clear_output() # Clear immediately on click
|
|
1703
|
+
with output_area:
|
|
1704
|
+
method = method_dropdown.value
|
|
1705
|
+
update_slider_values(method)
|
|
1706
|
+
print(f"Reset all parameters to defaults for {method}")
|
|
1707
|
+
|
|
1708
|
+
# Save function
|
|
1709
|
+
def save_plot(b):
|
|
1710
|
+
path = save_path_text.value
|
|
1711
|
+
if not path:
|
|
1712
|
+
with output_area:
|
|
1713
|
+
print("Please enter a save path")
|
|
1714
|
+
return
|
|
1715
|
+
if self.last_figure is None:
|
|
1716
|
+
with output_area:
|
|
1717
|
+
print("No plot to save. Run an analysis first.")
|
|
1718
|
+
return
|
|
1719
|
+
try:
|
|
1720
|
+
self.last_figure.savefig(path)
|
|
1721
|
+
with output_area:
|
|
1722
|
+
print(f"Plot saved to {path}")
|
|
1723
|
+
except Exception as e:
|
|
1724
|
+
with output_area:
|
|
1725
|
+
print(f"Error saving plot: {e}")
|
|
1726
|
+
|
|
1727
|
+
run_button.on_click(run_analysis)
|
|
1728
|
+
reset_button.on_click(reset_to_defaults)
|
|
1729
|
+
save_button.on_click(save_plot)
|
|
1730
|
+
|
|
1731
|
+
# Create main UI layout - matching synapse tuner structure
|
|
1732
|
+
ui = widgets.VBox([
|
|
1733
|
+
selection_row,
|
|
1734
|
+
button_row,
|
|
1735
|
+
section_row,
|
|
1736
|
+
param_columns,
|
|
1737
|
+
save_row
|
|
1738
|
+
], layout=widgets.Layout(padding='10px'))
|
|
1739
|
+
|
|
1740
|
+
# Display the interface - UI on top, output below (like synapse tuner)
|
|
1741
|
+
display(ui)
|
|
1742
|
+
display(output_area)
|
|
1743
|
+
|
|
1744
|
+
# Initial update
|
|
1745
|
+
update_params()
|
|
1746
|
+
|
|
1304
1747
|
|
|
1305
1748
|
# Example usage
|
|
1306
1749
|
# profiler = Profiler('./temp/templates', './temp/mechanisms/modfiles')
|