dendrotweaks 0.4.4__py3-none-any.whl → 0.4.5__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.
dendrotweaks/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.4.4"
1
+ __version__ = "0.4.5"
2
2
 
3
3
  from dendrotweaks.model import Model
4
4
  from dendrotweaks.simulators import NeuronSimulator
@@ -8,4 +8,5 @@ from dendrotweaks.analysis.ephys_analysis import plot_passive_properties
8
8
  from dendrotweaks.analysis.ephys_analysis import calculate_voltage_attenuation
9
9
  from dendrotweaks.analysis.ephys_analysis import plot_voltage_attenuation
10
10
  from dendrotweaks.analysis.ephys_analysis import calculate_dendritic_nonlinearity
11
- from dendrotweaks.analysis.ephys_analysis import plot_dendritic_nonlinearity
11
+ from dendrotweaks.analysis.ephys_analysis import plot_dendritic_nonlinearity
12
+ from dendrotweaks.analysis.ephys_analysis import calculate_sag_ratio
@@ -38,6 +38,11 @@ def get_somatic_data(model):
38
38
  def calculate_input_resistance(model):
39
39
  """
40
40
  Calculate the input resistance of the neuron model.
41
+
42
+ This function determines the input resistance by calculating the ratio
43
+ between the voltage change and the injected current. The voltage change
44
+ is measured as the difference between the membrane potential at the onset
45
+ and offset of the current injection.
41
46
 
42
47
  Parameters
43
48
  ----------
@@ -52,13 +57,11 @@ def calculate_input_resistance(model):
52
57
 
53
58
  v, t, dt, iclamp = get_somatic_data(model)
54
59
 
55
- v_min = np.min(v)
56
-
57
60
  amp = iclamp.amp
58
- start_ts = iclamp.delay / dt
59
- end_ts = int((iclamp.delay + iclamp.dur) / dt)
61
+ start_ts = int(iclamp.delay / dt)
62
+ stop_ts = int((iclamp.delay + iclamp.dur) / dt)
60
63
  v_onset = v[int(start_ts)]
61
- v_offset = v[int(end_ts)]
64
+ v_offset = v[int(stop_ts)]
62
65
 
63
66
  R_in = (v_onset - v_offset) / amp
64
67
  print(f"Input resistance: {R_in:.2f} MOhm")
@@ -71,44 +74,61 @@ def calculate_input_resistance(model):
71
74
  }
72
75
 
73
76
 
74
- def _exp_decay(t, A, tau):
75
- return A * np.exp(-t / tau)
76
-
77
+ def _double_exp_decay(t, A1, tau1, A2, tau2):
78
+ return A1 * np.exp(-t / tau1) + A2 * np.exp(-t / tau2)
77
79
 
78
80
  def calculate_time_constant(model):
79
81
  """
80
- Calculate the membrane time constant of the neuron model.
82
+ Estimate the passive membrane time constant (τm) by fitting
83
+ a double exponential to the somatic voltage decay and selecting
84
+ the slowest τ component.
81
85
 
82
86
  Parameters
83
87
  ----------
84
88
  model : Model
85
- The neuron model.
89
+ The neuron model (assumes Ih is disabled).
86
90
 
87
91
  Returns
88
92
  -------
89
93
  dict
90
- A dictionary containing the time constant and the exponential fit.
94
+ A dictionary with τm (slowest), both τs, and fit details.
91
95
  """
92
96
  v, t, dt, iclamp = get_somatic_data(model)
93
97
 
94
98
  start_ts = int(iclamp.delay / dt)
95
99
  stop_ts = int((iclamp.delay + iclamp.dur) / dt)
96
100
  min_ts = np.argmin(v[start_ts:stop_ts]) + start_ts
97
- v_min = np.min(v[start_ts: min_ts])
101
+ v_min = v[min_ts]
98
102
  v_decay = v[start_ts: min_ts] - v_min
99
103
  t_decay = t[start_ts: min_ts] - t[start_ts]
100
- popt, _ = curve_fit(_exp_decay, t_decay, v_decay, p0=[1, 100])
101
- tau = popt[1]
102
- A = popt[0]
103
- print(f"Membrane time constant: {tau:.2f} ms")
104
+
105
+ # Fit double exponential
106
+ try:
107
+ popt, _ = curve_fit(
108
+ _double_exp_decay, t_decay, v_decay,
109
+ p0=[1, 10, 0.5, 100],
110
+ bounds=(0, [np.inf, 1000, np.inf, 1000])
111
+ )
112
+ A1, tau1, A2, tau2 = popt
113
+ tau_slowest = max(tau1, tau2)
114
+ except RuntimeError:
115
+ print("Fit failed. Could not estimate time constant.")
116
+ return None
117
+
118
+ print(f"Time constant {tau_slowest:.2f} ms. Estimated from double exp fit (slowest component)")
119
+
104
120
  return {
105
- 'time_constant': tau,
106
- 'A': A,
121
+ 'time_constant': tau_slowest,
107
122
  'start_time': start_ts * dt,
123
+ 'tau1': tau1,
124
+ 'tau2': tau2,
125
+ 'A1': A1,
126
+ 'A2': A2,
108
127
  'decay_time': t_decay,
109
128
  'decay_voltage': v_decay
110
129
  }
111
130
 
131
+
112
132
  def calculate_passive_properties(model):
113
133
  """
114
134
  Calculate the passive properties of the neuron model.
@@ -140,7 +160,6 @@ def plot_passive_properties(data, ax=None):
140
160
  v_offset = data['offset_voltage']
141
161
  t_decay = data['decay_time']
142
162
  v_decay = data['decay_voltage']
143
- A = data['A']
144
163
  start_t = data['start_time']
145
164
 
146
165
  ax.set_title(f"R_in: {R_in:.2f} MOhm, Tau: {tau:.2f} ms")
@@ -148,8 +167,10 @@ def plot_passive_properties(data, ax=None):
148
167
  ax.axhline(v_offset, color='gray', linestyle='--', label='V offset')
149
168
 
150
169
  # Shift the exp_decay output along the y-axis
151
- shifted_exp_decay = _exp_decay(t_decay, A, tau) + v_offset
152
- ax.plot(t_decay + start_t, shifted_exp_decay, color='red', label='Exp. fit', linestyle='--')
170
+ fit_curve = _double_exp_decay(t_decay, data['A1'], data['tau1'], data['A2'], data['tau2']) + v_offset
171
+ label = f'Double exp fit (tau1 = {data["tau1"]:.1f} ms, tau2 = {data["tau2"]:.1f} ms)'
172
+
173
+ ax.plot(t_decay + start_t, fit_curve, color='red', label='Exp. fit', linestyle='--')
153
174
  ax.legend()
154
175
 
155
176
 
@@ -365,16 +386,18 @@ def calculate_voltage_attenuation(model):
365
386
 
366
387
 
367
388
  # Calculate voltage displacement from the resting potential
368
- delta_v_at_stimulated = voltage_at_stimulated[0] - np.min(voltage_at_stimulated)
369
- delta_vs = [v[0] - np.min(v) for v in voltages]
389
+ delta_v_at_stimulated = voltage_at_stimulated[0] - voltage_at_stimulated[-2]# np.min(voltage_at_stimulated)
390
+ delta_vs = [v[0] - v[-2] for v in voltages] # np.min(v) for v in voltages]
370
391
 
371
392
  min_voltages = [np.min(v) for v in voltages]
393
+ end_voltages = [v[-2] for v in voltages]
372
394
 
373
395
  attenuation = [dv / delta_v_at_stimulated for dv in delta_vs]
374
396
 
375
397
  return {
376
398
  'path_distances': path_distances,
377
399
  'min_voltages': min_voltages,
400
+ 'end_voltages': end_voltages,
378
401
  'attenuation': attenuation
379
402
  }
380
403
 
@@ -484,5 +507,36 @@ def plot_dendritic_nonlinearity(data, ax=None, **kwargs):
484
507
  ax[1].set_title('Dendritic nonlinearity')
485
508
 
486
509
 
510
+ def calculate_sag_ratio(model):
511
+ """
512
+ Calculate the sag ratio of the neuron model.
513
+
514
+ Parameters
515
+ ----------
516
+ model : Model
517
+ The neuron model.
518
+
519
+ Returns
520
+ -------
521
+ dict
522
+ A dictionary containing the sag ratio and intermediate values.
523
+ """
524
+ v, t, dt, iclamp = get_somatic_data(model)
487
525
 
526
+ start_ts = int(iclamp.delay / dt)
527
+ stop_ts = int((iclamp.delay + iclamp.dur) / dt)
528
+ min_ts = np.argmin(v[start_ts:stop_ts]) + start_ts
529
+ v_min = np.min(v[start_ts: min_ts])
530
+
531
+ a = v[stop_ts] - v_min
532
+ b = v[start_ts] - v_min
533
+
534
+ sag_ratio = a / b if b != 0 else np.nan
535
+
536
+ print(f"Sag ratio: {a:.2f}/{b:.2f} = {sag_ratio:.2f}")
537
+ return {
538
+ 'a': a,
539
+ 'b': b,
540
+ 'sag_ratio': sag_ratio,
541
+ }
488
542
 
@@ -0,0 +1,131 @@
1
+ # This Python channel class was automatically generated from a MOD file
2
+ # using DendroTweaks toolbox, dendrotweaks.dendrites.gr
3
+
4
+
5
+ from jaxley.channels import Channel
6
+ from jaxley.solver_gate import exponential_euler
7
+ import jax.numpy as np
8
+
9
+ class {{ class_name }}(Channel):
10
+ """
11
+ {{ title }}
12
+ """
13
+
14
+ def __init__(self, name="{{ class_name }}"):
15
+ self.current_is_in_mA_per_cm2 = True
16
+ super().__init__(name=name)
17
+ self.channel_params = {
18
+ {% for param, value in channel_params.items() -%}
19
+ "{{ param }}_{{ class_name }}": {{ value }}
20
+ {%- if not loop.last -%},
21
+ {%- endif %}
22
+ {% endfor -%}
23
+ }
24
+ self.channel_states = {
25
+ {% for state in state_vars -%}
26
+ "{{ state }}_{{class_name}}": 0.0
27
+ {%- if not loop.last %},
28
+ {%- endif %}
29
+ {% endfor -%}
30
+ }
31
+ self._state_powers = {
32
+ {% for state, power in state_vars.items() -%}
33
+ "{{ state }}_{{class_name}}": {{ power }}
34
+ {%- if not loop.last %},
35
+ {%- endif %}
36
+ {% endfor -%}
37
+ }
38
+ self.ion = "{{ ion }}"
39
+ self.current_name = "i_{{ ion }}"
40
+
41
+ self.independent_var_name = "{{ independent_var_name }}"
42
+ self.tadj = 1
43
+
44
+ def set_tadj(self, temperature):
45
+ """
46
+ Set the temperature adjustment factor for the channel kinetics.
47
+
48
+ Parameters
49
+ ----------
50
+ temperature : float
51
+ The temperature in degrees Celsius.
52
+
53
+ Notes
54
+ -----
55
+ The temperature adjustment factor is calculated as:
56
+ tadj = q10 ** ((temperature - reference_temp) / 10)
57
+ where q10 is the temperature coefficient and reference_temp is the
58
+ temperature at which the channel kinetics were measured.
59
+ """
60
+ q10 = self.channel_params.get(f"q10_{{ class_name }}")
61
+ reference_temp = self.channel_params.get(f"temp_{{ class_name }}")
62
+ if q10 is None or reference_temp is None:
63
+ self.tadj = 1
64
+ print(f"Warning: q10 or reference temperature not set for {self.name}. Using default tadj = 1.")
65
+ else:
66
+ self.tadj = q10 ** ((temperature - reference_temp) / 10)
67
+
68
+ def __getitem__(self, item):
69
+ return self.channel_params[item]
70
+
71
+ def __setitem__(self, item, value):
72
+ self.channel_params[item] = value
73
+
74
+ {% for function in functions %}
75
+ {{ function['signature'] }}
76
+ {%- for param in function['params'] -%}
77
+ {{ param }} = self.channel_params.get("{{ param }}_{{ class_name }}", 1)
78
+ {% endfor %}
79
+ {{ function['body'] }}
80
+ {% if not loop.last %}
81
+ {% endif %}{% endfor -%}
82
+ {% for procedure in procedures %}
83
+ {{ procedure['signature'] }}
84
+ {% for param in procedure['params'] -%}
85
+ {{ param }} = self.channel_params.get("{{ param }}_{{ class_name }}", 1)
86
+ {% endfor %}
87
+ {{ procedure['body'] }}
88
+ {%- if not loop.last %}
89
+ {% endif %}{% endfor %}
90
+
91
+ def update_states(self, states, dt, v, params):
92
+ {% for state, state_params in state_vars.items() -%}
93
+ {{state}} = states['{{ state }}_{{class_name}}']
94
+ {%- if not loop.last %}
95
+ {%- endif %}
96
+ {% endfor -%}
97
+ {{- procedure_calls}}
98
+ {% for state in state_vars.keys() %}new_{{state}} = exponential_euler({{state}}, dt, {{state}}Inf, {{state}}Tau){% if not loop.last %}
99
+ {% endif %}{% endfor %}
100
+ return {
101
+ {% for state in state_vars -%}
102
+ "{{ state }}_{{class_name}}": new_{{state}}
103
+ {%- if not loop.last %},
104
+ {%- endif %}
105
+ {% endfor -%}
106
+ }
107
+
108
+ def compute_current(self, states, v, params):
109
+ {% for state in state_vars.keys() -%}
110
+ {{state}} = states['{{ state }}_{{class_name}}']
111
+ {%- if not loop.last %}
112
+ {%- endif %}
113
+ {% endfor -%}
114
+ gbar = params["gbar_{{class_name}}"]
115
+ # E = params["E_{{ ion }}"]
116
+ E = {{ E_ion }}
117
+ {{ procedure_calls}}
118
+ g = self.tadj * gbar *{% for state, power in state_vars.items()%} {{state}}**{{power['power']}} {% if not loop.last %}*{% endif %}{% endfor %}
119
+ return g * (v - E)
120
+
121
+ def init_state(self, states, v, params, delta_t):
122
+ {{ procedure_calls}}
123
+ return {
124
+ {% for state in state_vars.keys() -%}
125
+ "{{ state }}_{{class_name}}": {{state}}Inf
126
+ {%- if not loop.last %},
127
+ {%- endif %}
128
+ {% endfor -%}
129
+ }
130
+
131
+
dendrotweaks/model.py CHANGED
@@ -1421,12 +1421,15 @@ class Model():
1421
1421
  domains_to_mechs = {domain_name: mech_names for domain_name, mech_names
1422
1422
  in self.domains_to_mechs.items() if domain_name in [domain.name for domain in domains_in_subtree]}
1423
1423
  common_mechs = set.intersection(*domains_to_mechs.values())
1424
- if not common_mechs:
1424
+ if not all(mech_names == common_mechs
1425
+ for mech_names in domains_to_mechs.values()):
1425
1426
  raise ValueError(
1426
1427
  'The domains in the subtree have different mechanisms. '
1427
1428
  'Please ensure that all domains in the subtree have the same mechanisms. '
1428
1429
  'You may need to insert the missing mechanisms and set their conductances to 0 where they are not needed.'
1429
1430
  )
1431
+ elif len(domains_in_subtree) == 1:
1432
+ common_mechs = self.domains_to_mechs[domain_name].copy()
1430
1433
 
1431
1434
  inserted_mechs = {mech_name: mech for mech_name, mech
1432
1435
  in self.mechanisms.items()
@@ -1496,9 +1499,15 @@ class Model():
1496
1499
 
1497
1500
  root_segs = [seg for seg in root.segments]
1498
1501
  params_to_coeffs = {}
1499
- for param_name in self.params:
1500
- coeffs = self.fit_distribution(param_name, segments=root_segs, plot=False)
1501
- params_to_coeffs[param_name] = coeffs
1502
+ # for param_name in self.params:
1503
+ common_mechs.add('Independent')
1504
+ for mech in common_mechs:
1505
+ for param_name in self.mechs_to_params[mech]:
1506
+ coeffs = self.fit_distribution(param_name, segments=root_segs, plot=False)
1507
+ if coeffs is None:
1508
+ warnings.warn(f'Cannot fit distribution for parameter {param_name}. No values found.')
1509
+ continue
1510
+ params_to_coeffs[param_name] = coeffs
1502
1511
 
1503
1512
 
1504
1513
  # Create new domain
@@ -1535,9 +1544,12 @@ class Model():
1535
1544
  }
1536
1545
 
1537
1546
 
1538
- def fit_distribution(self, param_name, segments, max_degree=6, tolerance=1e-7, plot=False):
1547
+ def fit_distribution(self, param_name, segments, max_degree=20, tolerance=1e-7, plot=False):
1539
1548
  from numpy import polyfit, polyval
1540
1549
  values = [seg.get_param_value(param_name) for seg in segments]
1550
+ # if all values are NaN, return None
1551
+ if all(np.isnan(values)):
1552
+ return None
1541
1553
  distances = [seg.path_distance() for seg in segments]
1542
1554
  sorted_pairs = sorted(zip(distances, values))
1543
1555
  distances, values = zip(*sorted_pairs)
@@ -1564,7 +1576,7 @@ class Model():
1564
1576
  elif len(coeffs) == 2:
1565
1577
  self.params[param_name][group_name] = Distribution('linear', slope=coeffs[0], intercept=coeffs[1])
1566
1578
  else:
1567
- self.params[param_name][group_name] = Distribution('polynomial', coeffs=coeffs)
1579
+ self.params[param_name][group_name] = Distribution('polynomial', coeffs=coeffs.tolist())
1568
1580
 
1569
1581
 
1570
1582
  # ========================================================================
@@ -1905,13 +1917,13 @@ class Model():
1905
1917
 
1906
1918
  df_iclamps = df_stimuli[df_stimuli['type'] == 'iclamp'].reset_index(drop=True, inplace=False)
1907
1919
 
1908
- for i, row in df_iclamps.iterrows():
1920
+ for row in df_iclamps.itertuples(index=False):
1909
1921
  self.add_iclamp(
1910
- self.sec_tree.sections[row['sec_idx']],
1911
- row['loc'],
1912
- data['stimuli']['iclamps'][i]['amp'],
1913
- data['stimuli']['iclamps'][i]['delay'],
1914
- data['stimuli']['iclamps'][i]['dur']
1922
+ self.sec_tree.sections[row.sec_idx],
1923
+ row.loc,
1924
+ data['stimuli']['iclamps'][row.idx]['amp'],
1925
+ data['stimuli']['iclamps'][row.idx]['delay'],
1926
+ data['stimuli']['iclamps'][row.idx]['dur']
1915
1927
  )
1916
1928
 
1917
1929
  # Populations -------------------------------------------------------
@@ -1944,15 +1956,10 @@ class Model():
1944
1956
  # Recordings ---------------------------------------------------------
1945
1957
 
1946
1958
  df_recs = df_stimuli[df_stimuli['type'] == 'rec'].reset_index(drop=True, inplace=False)
1947
- for i, row in df_recs.iterrows():
1948
- # TODO: This conditional statement is to account for a recent change
1949
- # in the JSON structure. It should be removed in the future.
1950
- if data['stimuli'].get('recordings'):
1951
- var = data['stimuli']['recordings'][i]['var']
1952
- else:
1953
- var = 'v'
1959
+ for row in df_recs.itertuples(index=False):
1960
+ var = data['stimuli']['recordings'][row.idx]['var']
1954
1961
  self.add_recording(
1955
- self.sec_tree.sections[row['sec_idx']], row['loc'], var
1962
+ self.sec_tree.sections[row.sec_idx], row.loc, var
1956
1963
  )
1957
1964
 
1958
1965
 
@@ -36,8 +36,8 @@ def create_point_tree(source: Union[str, DataFrame]) -> PointTree:
36
36
  raise ValueError("Source must be a file path (str) or a DataFrame.")
37
37
 
38
38
  nodes = [
39
- Point(row['Index'], row['Type'], row['X'], row['Y'], row['Z'], row['R'], row['Parent'])
40
- for _, row in df.iterrows()
39
+ Point(row.Index, row.Type, row.X, row.Y, row.Z, row.R, row.Parent)
40
+ for row in df.itertuples(index=False)
41
41
  ]
42
42
  point_tree = PointTree(nodes)
43
43
  point_tree.remove_overlaps()
@@ -35,7 +35,16 @@ class SWCReader():
35
35
  header=None,
36
36
  comment='#',
37
37
  names=['Index', 'Type', 'X', 'Y', 'Z', 'R', 'Parent'],
38
- index_col=False
38
+ index_col=False,
39
+ dtype={
40
+ 'Index': int,
41
+ 'Type': int,
42
+ 'X': float,
43
+ 'Y': float,
44
+ 'Z': float,
45
+ 'R': float,
46
+ 'Parent': int
47
+ }
39
48
  )
40
49
 
41
50
  if (df['R'] == 0).all():
@@ -59,7 +59,7 @@ class Point(Node):
59
59
  x: float, y: float, z: float, r: float,
60
60
  parent_idx: str) -> None:
61
61
  super().__init__(idx, parent_idx)
62
- self.type_idx = type_idx
62
+ self.type_idx = int(type_idx)
63
63
  self.x = x
64
64
  self.y = y
65
65
  self.z = z
dendrotweaks/utils.py CHANGED
@@ -182,31 +182,54 @@ def read_file(path_to_file):
182
182
  return content
183
183
 
184
184
 
185
- def download_example_data(path_to_destination):
185
+ def download_example_data(path_to_destination, include_templates=True, include_modfiles=True):
186
186
  """
187
- Download the examples subfolder from the DendroTweaks GitHub repository.
187
+ Download and extract specific folders from the DendroTweaks GitHub repository:
188
+ - examples/ <- from examples subfolder (always included)
189
+ - examples/Templates/ <- from src/dendrotweaks/biophys/default_templates (optional)
190
+ - examples/Default/ <- from src/dendrotweaks/biophys/default_mod (optional)
188
191
 
189
192
  Parameters
190
193
  ----------
191
194
  path_to_destination : str
192
- The path to the destination folder where the examples will be downloaded.
195
+ The path to the destination folder where the data will be downloaded and extracted.
196
+
197
+ include_templates : bool, optional
198
+ If True, also extract default_templates/ into examples/Templates/.
199
+
200
+ include_modfiles : bool, optional
201
+ If True, also extract default_mod/ into examples/Default/.
193
202
  """
194
203
  if not os.path.exists(path_to_destination):
195
204
  os.makedirs(path_to_destination)
196
205
 
197
206
  repo_url = "https://github.com/Poirazi-Lab/DendroTweaks/archive/refs/heads/main.zip"
198
- zip_path = os.path.join(path_to_destination, "examples.zip")
207
+ zip_path = os.path.join(path_to_destination, "dendrotweaks_repo.zip")
199
208
 
200
- print(f"Downloading examples from {repo_url}...")
209
+ print(f"Downloading data from {repo_url}...")
201
210
  urllib.request.urlretrieve(repo_url, zip_path)
202
211
 
203
- print(f"Extracting examples to {path_to_destination}")
212
+ print(f"Extracting relevant folders to {path_to_destination}...")
204
213
  with zipfile.ZipFile(zip_path, 'r') as zip_ref:
205
214
  for member in zip_ref.namelist():
215
+ target_path = None
216
+
217
+ # === Always extract examples/ folder ===
206
218
  if member.startswith("DendroTweaks-main/examples/"):
207
- # Extract the file with the correct path
208
- member_path = os.path.relpath(member, "DendroTweaks-main/examples")
209
- target_path = os.path.join(path_to_destination, member_path)
219
+ rel_path = os.path.relpath(member, "DendroTweaks-main/examples")
220
+ target_path = os.path.join(path_to_destination, rel_path)
221
+
222
+ # === Optionally extract Templates/ folder ===
223
+ elif include_templates and member.startswith("DendroTweaks-main/src/dendrotweaks/biophys/default_templates/"):
224
+ rel_path = os.path.relpath(member, "DendroTweaks-main/src/dendrotweaks/biophys/default_templates")
225
+ target_path = os.path.join(path_to_destination, "Templates", rel_path)
226
+
227
+ # === Optionally extract Default/ folder ===
228
+ elif include_modfiles and member.startswith("DendroTweaks-main/src/dendrotweaks/biophys/default_mod/"):
229
+ rel_path = os.path.relpath(member, "DendroTweaks-main/src/dendrotweaks/biophys/default_mod")
230
+ target_path = os.path.join(path_to_destination, "Default", rel_path)
231
+
232
+ if target_path:
210
233
  if member.endswith('/'):
211
234
  os.makedirs(target_path, exist_ok=True)
212
235
  else:
@@ -214,8 +237,8 @@ def download_example_data(path_to_destination):
214
237
  with zip_ref.open(member) as source, open(target_path, 'wb') as target:
215
238
  target.write(source.read())
216
239
 
217
- os.remove(zip_path) # Clean up the zip file
218
- print(f"Examples downloaded successfully to {path_to_destination}/.")
240
+ os.remove(zip_path)
241
+ print(f"Data downloaded and extracted successfully to {path_to_destination}/.")
219
242
 
220
243
 
221
244
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dendrotweaks
3
- Version: 0.4.4
3
+ Version: 0.4.5
4
4
  Summary: A toolbox for exploring dendritic dynamics
5
5
  Home-page: https://dendrotweaks.dendrites.gr
6
6
  Author: Roman Makarov
@@ -1,11 +1,11 @@
1
- dendrotweaks/__init__.py,sha256=Z2JpwnO9nBPrkR1w0u_6gWnCJetIhTx_27PehYlfTRM,384
2
- dendrotweaks/model.py,sha256=IjmLrp-Ooh1WLdTb7Xg_9q2KUO7Xuokll26NQyVITlk,70965
1
+ dendrotweaks/__init__.py,sha256=wAjTFbihG1E0dDURosjTFhIilebuX2iXvefW8BYoDYQ,384
2
+ dendrotweaks/model.py,sha256=6yvsIu-DImsBNosJmqJt6n59vaFMtRsDog4wAWppwlY,71362
3
3
  dendrotweaks/model_io.py,sha256=xwXKMcUle-Y0HoWFYVZu3G8v4pdQXmeaDfl2Xi65eHw,2137
4
4
  dendrotweaks/path_manager.py,sha256=dai5o6UA0nk-ubwKWRu4LFdDBO77zW_SsMf6k0MLBiI,8703
5
5
  dendrotweaks/simulators.py,sha256=OscZ4H6z9YiNloDtMAgpPx9n2e-9WJyLEWtcSD1YZR8,7411
6
- dendrotweaks/utils.py,sha256=jaUJNb39Bsevg3WJByP56bO7CLj1wzlh-uGZl-lxi1I,7131
7
- dendrotweaks/analysis/__init__.py,sha256=SEYpoQ5iXiQXyHB20-IAdDHYI-7CR5GYFXIwr-O05Ug,858
8
- dendrotweaks/analysis/ephys_analysis.py,sha256=PqT3aBCuxQbvdm9jnXjlTJ3R5n7_Vwp4fLrHGgtfoWw,14362
6
+ dendrotweaks/utils.py,sha256=KrGV8972sCqn-OKiOpr2KOcEekcHnP10Tl499z5M60k,8477
7
+ dendrotweaks/analysis/__init__.py,sha256=tDi4BHtW1fX5iiZVk1OM3gmLo69VFmUN8AEK_F8tDq4,927
8
+ dendrotweaks/analysis/ephys_analysis.py,sha256=EuT7bHoSAjNg4736iKxj2Y1UfI2TfeWeoxtvUe78zfw,16188
9
9
  dendrotweaks/analysis/morphometric_analysis.py,sha256=5zohjGssyx-wezI-yY3Q-kYM_wzAQLLFBJ9Xk950_JY,3571
10
10
  dendrotweaks/biophys/__init__.py,sha256=k0o2xwyoaJUb1lfO9OHtqxheNP6R-Ya5o0g-bJOdCZg,360
11
11
  dendrotweaks/biophys/distributions.py,sha256=XGczxBYJ0-vkIfXbfzvlIqlH9OpBuT9J95Kzyd2zL5A,10325
@@ -20,6 +20,7 @@ dendrotweaks/biophys/default_mod/NMDA.mod,sha256=tT4Q5UPoeztXcQ45uZc2PUO3-8OkDLC
20
20
  dendrotweaks/biophys/default_mod/vecstim.mod,sha256=iSpJgR96O2Z3pLNUFIsZ7YJ529ncKUBaZqDJvA0_oV0,965
21
21
  dendrotweaks/biophys/default_templates/NEURON_template.py,sha256=MWSv2fLKGJxdt2zfSO0b74peuBC8U9j_S6AwM5URXts,14945
22
22
  dendrotweaks/biophys/default_templates/default.py,sha256=7HEbR2GJEOhgiox1QtZUEuHi5ihNAHDLsXQiQk980tI,2201
23
+ dendrotweaks/biophys/default_templates/jaxley.py,sha256=RFmWyLOwa0GCjkuaT-gBbjVrniu99DD3PPAs3J9os3E,4599
23
24
  dendrotweaks/biophys/default_templates/standard_channel.mod,sha256=sw80c-JyqfXNA7c7v7pZGLY-0MgFUvd3bPvJcAGXNSk,2923
24
25
  dendrotweaks/biophys/default_templates/template_jaxley.py,sha256=t-GsCSUyQ7rDoaLmyuWd9bIxB8W3bCqJdnikD59EVvI,3676
25
26
  dendrotweaks/biophys/default_templates/template_jaxley_new.py,sha256=I62KhnOYNV1bT-nPsDTxjIISYmDcso2X8rnsos28nYs,3631
@@ -34,13 +35,13 @@ dendrotweaks/biophys/io/parser.py,sha256=boT27lFrn5LYrJnkZFs0SwrZZrkSkwO8efqGPJ4
34
35
  dendrotweaks/biophys/io/reader.py,sha256=JWm5WM9illvSfDkhWEmWBcj8Y7PSi8zeZX9j1ARUHVU,6576
35
36
  dendrotweaks/morphology/__init__.py,sha256=JwXmSmdn9e_jqslITEdiU9kWvzxcxT9Aw_kUkXLbm5o,353
36
37
  dendrotweaks/morphology/domains.py,sha256=l57KVR5eo1LlH_fCd1AOMiG_SsYLBPBTGQ5R78BHfdM,2545
37
- dendrotweaks/morphology/point_trees.py,sha256=5dUPaQXYPdJbWoD3pFI2DV2XnuFRhB5d0wTBlfmmIeI,21600
38
+ dendrotweaks/morphology/point_trees.py,sha256=aZl5p6FGn6mph9xNo-3L_HC7mIvLhvi3BLJuhuihssk,21605
38
39
  dendrotweaks/morphology/sec_trees.py,sha256=eKLC-yNhsn_rPdTE7w7p6STa1onYkBTGcpBKBpEWZUI,36957
39
40
  dendrotweaks/morphology/seg_trees.py,sha256=-XeSJuD7ZixBJYQDzvmSEiNvOWbVmX_DanyAPkkR-NA,4042
40
41
  dendrotweaks/morphology/trees.py,sha256=NrNvPMR-U0clt63eqwVJqU0H8NJgY53QGA_BkdcwkQI,16033
41
42
  dendrotweaks/morphology/io/__init__.py,sha256=gAZqZdf5VKPb6ksK8Lwt7MbTAq8TDP8uq3Vs_ebNFEY,324
42
- dendrotweaks/morphology/io/factories.py,sha256=DCE37QCloiYVro5HGihJbxPz91BB3y5NNf-oRaQ-M2g,6383
43
- dendrotweaks/morphology/io/reader.py,sha256=hW3c541WtG1rNag_YreEhvrLzm8-OTtw0fQREeHDthM,1913
43
+ dendrotweaks/morphology/io/factories.py,sha256=YkTCXWuJ6bwUW0kjcV2TSzu43EvkE8N5yC3q-B02kxc,6372
44
+ dendrotweaks/morphology/io/reader.py,sha256=R368nkHfAKHdSnqg7jjc_us7gVSOlMJpzAjhUUU7Wfw,2121
44
45
  dendrotweaks/morphology/io/validation.py,sha256=lVkYw9y9yG5QpRh_N0YQ3FbZwuSUsQfSqJTMumMcDdc,7872
45
46
  dendrotweaks/morphology/reduce/__init__.py,sha256=p6Mg3KDHxTt8S4DtI0m7L7MqV6dS2pdIYAwB7B-toVw,921
46
47
  dendrotweaks/morphology/reduce/reduce.py,sha256=5czZDrG3xsvHn3c_tbYhUOlXgST989-RS-ntbhlvvA0,6361
@@ -49,8 +50,8 @@ dendrotweaks/stimuli/__init__.py,sha256=bFfSEZhCVpwOVEBgLe65iiY3SdpjKPhyLemC1z5O
49
50
  dendrotweaks/stimuli/iclamps.py,sha256=NjkhhwZKJR1f_g3N9BVxMVoO9ubBk5WkQ6h9Bnf9xgA,1681
50
51
  dendrotweaks/stimuli/populations.py,sha256=vq2NUOaxaltUwlcT7wuCe8z1JWc8rk6HfPmfEd1kK68,8335
51
52
  dendrotweaks/stimuli/synapses.py,sha256=g4MgWTske2TZ2i9FIIOE8-KXNx_3dWa3zEhB2rcqYig,5470
52
- dendrotweaks-0.4.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
53
- dendrotweaks-0.4.4.dist-info/METADATA,sha256=2eqzr1zuEY9kyPT0X0ggJ5m_lAqV2_sQKEtSX9TA9PQ,2740
54
- dendrotweaks-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
- dendrotweaks-0.4.4.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
56
- dendrotweaks-0.4.4.dist-info/RECORD,,
53
+ dendrotweaks-0.4.5.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
54
+ dendrotweaks-0.4.5.dist-info/METADATA,sha256=B834Ro9ggHwzr3Ma4FE-twhlZ_v--ZHd-uvUk8HI1SQ,2740
55
+ dendrotweaks-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ dendrotweaks-0.4.5.dist-info/top_level.txt,sha256=OzT_2BSI5j5zxC447K6Y-0W-GHbued7iX-_hFGAKMxY,13
57
+ dendrotweaks-0.4.5.dist-info/RECORD,,