dendrotweaks 0.3.1__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 +10 -0
- dendrotweaks/analysis/__init__.py +11 -0
- dendrotweaks/analysis/ephys_analysis.py +482 -0
- dendrotweaks/analysis/morphometric_analysis.py +106 -0
- dendrotweaks/membrane/__init__.py +6 -0
- dendrotweaks/membrane/default_mod/AMPA.mod +65 -0
- dendrotweaks/membrane/default_mod/AMPA_NMDA.mod +100 -0
- dendrotweaks/membrane/default_mod/CaDyn.mod +54 -0
- dendrotweaks/membrane/default_mod/GABAa.mod +65 -0
- dendrotweaks/membrane/default_mod/Leak.mod +27 -0
- dendrotweaks/membrane/default_mod/NMDA.mod +72 -0
- dendrotweaks/membrane/default_mod/vecstim.mod +76 -0
- dendrotweaks/membrane/default_templates/NEURON_template.py +354 -0
- dendrotweaks/membrane/default_templates/default.py +73 -0
- dendrotweaks/membrane/default_templates/standard_channel.mod +87 -0
- dendrotweaks/membrane/default_templates/template_jaxley.py +108 -0
- dendrotweaks/membrane/default_templates/template_jaxley_new.py +108 -0
- dendrotweaks/membrane/distributions.py +324 -0
- dendrotweaks/membrane/groups.py +103 -0
- dendrotweaks/membrane/io/__init__.py +11 -0
- dendrotweaks/membrane/io/ast.py +201 -0
- dendrotweaks/membrane/io/code_generators.py +312 -0
- dendrotweaks/membrane/io/converter.py +108 -0
- dendrotweaks/membrane/io/factories.py +144 -0
- dendrotweaks/membrane/io/grammar.py +417 -0
- dendrotweaks/membrane/io/loader.py +90 -0
- dendrotweaks/membrane/io/parser.py +499 -0
- dendrotweaks/membrane/io/reader.py +212 -0
- dendrotweaks/membrane/mechanisms.py +574 -0
- dendrotweaks/model.py +1916 -0
- dendrotweaks/model_io.py +75 -0
- dendrotweaks/morphology/__init__.py +5 -0
- dendrotweaks/morphology/domains.py +100 -0
- dendrotweaks/morphology/io/__init__.py +5 -0
- dendrotweaks/morphology/io/factories.py +212 -0
- dendrotweaks/morphology/io/reader.py +66 -0
- dendrotweaks/morphology/io/validation.py +212 -0
- dendrotweaks/morphology/point_trees.py +681 -0
- dendrotweaks/morphology/reduce/__init__.py +16 -0
- dendrotweaks/morphology/reduce/reduce.py +155 -0
- dendrotweaks/morphology/reduce/reduced_cylinder.py +129 -0
- dendrotweaks/morphology/sec_trees.py +1112 -0
- dendrotweaks/morphology/seg_trees.py +157 -0
- dendrotweaks/morphology/trees.py +567 -0
- dendrotweaks/path_manager.py +261 -0
- dendrotweaks/simulators.py +235 -0
- dendrotweaks/stimuli/__init__.py +3 -0
- dendrotweaks/stimuli/iclamps.py +73 -0
- dendrotweaks/stimuli/populations.py +265 -0
- dendrotweaks/stimuli/synapses.py +203 -0
- dendrotweaks/utils.py +239 -0
- dendrotweaks-0.3.1.dist-info/METADATA +70 -0
- dendrotweaks-0.3.1.dist-info/RECORD +56 -0
- dendrotweaks-0.3.1.dist-info/WHEEL +5 -0
- dendrotweaks-0.3.1.dist-info/licenses/LICENSE +674 -0
- dendrotweaks-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
# This Python channel class was automatically generated from a MOD file
|
2
|
+
# using DendroTweaks toolbox, dendrotweaks.dendrites.gr
|
3
|
+
|
4
|
+
from jaxley.channels import Channel
|
5
|
+
from jaxley.solver_gate import exponential_euler
|
6
|
+
import jax.numpy as jn
|
7
|
+
|
8
|
+
class {{ class_name }}(Channel):
|
9
|
+
"""
|
10
|
+
{{ title }}
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(self, name="{{ class_name }}"):
|
14
|
+
super().__init__(name=name)
|
15
|
+
self.params = {
|
16
|
+
{% for param, value in channel_params.items() -%}
|
17
|
+
"{{{{ param }}": {{ value }}
|
18
|
+
{%- if not loop.last -%},
|
19
|
+
{%- endif %}
|
20
|
+
{% endfor -%}
|
21
|
+
}
|
22
|
+
self.states = {
|
23
|
+
{% for state in state_vars -%}
|
24
|
+
"{{ state }}": 0.0
|
25
|
+
{%- if not loop.last %},
|
26
|
+
{%- endif %}
|
27
|
+
{% endfor -%}
|
28
|
+
}
|
29
|
+
self._state_powers = {
|
30
|
+
{% for state, power in state_vars.items() -%}
|
31
|
+
"{{class_name}}_{{ state }}": {{ power }}
|
32
|
+
{%- if not loop.last %},
|
33
|
+
{%- endif %}
|
34
|
+
{% endfor -%}
|
35
|
+
}
|
36
|
+
self.ion = "{{ ion }}"
|
37
|
+
self.current_name = "i_{{ ion }}"
|
38
|
+
|
39
|
+
self.independent_var_name = "{{ independent_var_name }}"
|
40
|
+
|
41
|
+
# @property
|
42
|
+
# def tadj(self):
|
43
|
+
# return self.tadj = q10 ** ((celsius - temp) / 10)
|
44
|
+
|
45
|
+
def __getitem__(self, item):
|
46
|
+
return self.channel_params[item]
|
47
|
+
|
48
|
+
def __setitem__(self, item, value):
|
49
|
+
self.channel_params[item] = value
|
50
|
+
|
51
|
+
{%- for function in functions %}
|
52
|
+
{{ function['signature'] }}
|
53
|
+
{%- for param in function['params'] -%}
|
54
|
+
{{ param }} = self.channel_params.get("{{ class_name }}_{{ param }}", 1)
|
55
|
+
{% endfor %}
|
56
|
+
{{ function['body'] }}
|
57
|
+
{% if not loop.last %}
|
58
|
+
{% endif %}{% endfor -%}
|
59
|
+
{% for procedure in procedures %}
|
60
|
+
{{ procedure['signature'] }}
|
61
|
+
{% for param in procedure['params'] -%}
|
62
|
+
{{ param }} = self.channel_params.get("{{ class_name }}_{{ param }}", 1)
|
63
|
+
{% endfor %}
|
64
|
+
{{ procedure['body'] }}
|
65
|
+
{%- if not loop.last %}
|
66
|
+
{% endif %}{% endfor %}
|
67
|
+
|
68
|
+
def update_states(self, states, dt, v, params):
|
69
|
+
{% for state, state_params in state_vars.items() -%}
|
70
|
+
{{state}} = states['{{class_name}}_{{state}}']
|
71
|
+
{%- if not loop.last %}
|
72
|
+
{%- endif %}
|
73
|
+
{% endfor -%}
|
74
|
+
{{- procedure_calls}}
|
75
|
+
{% for state in state_vars.keys() %}new_{{state}} = exponential_euler({{state}}, dt, {{state}}Inf, {{state}}Tau){% if not loop.last %}
|
76
|
+
{% endif %}{% endfor %}
|
77
|
+
return {
|
78
|
+
{% for state in state_vars -%}
|
79
|
+
"{{class_name}}_{{state}}": new_{{state}}
|
80
|
+
{%- if not loop.last %},
|
81
|
+
{%- endif %}
|
82
|
+
{% endfor -%}
|
83
|
+
}
|
84
|
+
|
85
|
+
def compute_current(self, states, v, params):
|
86
|
+
{% for state in state_vars.keys() -%}
|
87
|
+
{{state}} = states['{{class_name}}_{{state}}']
|
88
|
+
{%- if not loop.last %}
|
89
|
+
{%- endif %}
|
90
|
+
{% endfor -%}
|
91
|
+
gbar = params["{{class_name}}_gbar"]
|
92
|
+
# E = params["E_{{ ion }}"]
|
93
|
+
E = {{ E_ion }}
|
94
|
+
{{ procedure_calls}}
|
95
|
+
g = self.tadj * gbar *{% for state, power in state_vars.items()%} {{state}}**{{power}} {% if not loop.last %}*{% endif %}{% endfor %}* 1000
|
96
|
+
return g * (v - E)
|
97
|
+
|
98
|
+
def init_state(self, states, v, params, delta_t):
|
99
|
+
{{ procedure_calls}}
|
100
|
+
return {
|
101
|
+
{% for state in state_vars.keys() -%}
|
102
|
+
"{{class_name}}_{{state}}": {{state}}Inf
|
103
|
+
{%- if not loop.last %},
|
104
|
+
{%- endif %}
|
105
|
+
{% endfor -%}
|
106
|
+
}
|
107
|
+
|
108
|
+
|
@@ -0,0 +1,324 @@
|
|
1
|
+
from typing import Callable, Dict, List
|
2
|
+
from numpy import ndarray, full_like
|
3
|
+
from numpy import exp, sin, polyval
|
4
|
+
import warnings
|
5
|
+
# Define simple functions and store them alongside their defaults in FUNCTIONS
|
6
|
+
def constant(position, value=0):
|
7
|
+
"""
|
8
|
+
Constant function that returns a constant value for any position.
|
9
|
+
|
10
|
+
Parameters
|
11
|
+
----------
|
12
|
+
position : float or numpy.ndarray
|
13
|
+
The position at which to evaluate the function.
|
14
|
+
value : float
|
15
|
+
The constant value to return.
|
16
|
+
|
17
|
+
Returns
|
18
|
+
-------
|
19
|
+
float or numpy.ndarray
|
20
|
+
The value of the constant function at the given position.
|
21
|
+
"""
|
22
|
+
if isinstance(position, ndarray):
|
23
|
+
return full_like(position, value)
|
24
|
+
else:
|
25
|
+
return value
|
26
|
+
|
27
|
+
|
28
|
+
def uniform(position, value=0):
|
29
|
+
"""
|
30
|
+
Constant function that returns a constant value for any position.
|
31
|
+
|
32
|
+
Parameters
|
33
|
+
----------
|
34
|
+
position : float or numpy.ndarray
|
35
|
+
The position at which to evaluate the function.
|
36
|
+
value : float
|
37
|
+
The constant value to return.
|
38
|
+
|
39
|
+
Returns
|
40
|
+
-------
|
41
|
+
float or numpy.ndarray
|
42
|
+
The value of the constant function at the given position.
|
43
|
+
"""
|
44
|
+
if isinstance(position, ndarray):
|
45
|
+
return full_like(position, value)
|
46
|
+
else:
|
47
|
+
return value
|
48
|
+
|
49
|
+
|
50
|
+
def linear(position, slope=1, intercept=0):
|
51
|
+
"""
|
52
|
+
Linear function that returns a linearly changing value for any position.
|
53
|
+
|
54
|
+
Parameters
|
55
|
+
----------
|
56
|
+
position : float or numpy.ndarray
|
57
|
+
The position at which to evaluate the function.
|
58
|
+
slope : float
|
59
|
+
The slope of the linear function.
|
60
|
+
intercept : float
|
61
|
+
The intercept of the linear function.
|
62
|
+
|
63
|
+
Returns
|
64
|
+
-------
|
65
|
+
float or numpy.ndarray
|
66
|
+
The value of the linear function at the given position.
|
67
|
+
"""
|
68
|
+
return slope * position + intercept
|
69
|
+
|
70
|
+
def exponential(distance: float, vertical_shift:float = 0, scale_factor: float =1, growth_rate: float=1, horizontal_shift: float = 0) -> float:
|
71
|
+
"""
|
72
|
+
Exponential distribution function.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
distance (float): The distance parameter.
|
76
|
+
vertical_shift (float): The vertical shift parameter.
|
77
|
+
scale_factor (float): The scale factor parameter.
|
78
|
+
growth_rate (float): The growth rate parameter.
|
79
|
+
horizontal_shift (float): The horizontal shift parameter.
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
The result of the exponential equation: vertical_shift + scale_factor * exp(growth_rate * (distance - horizontal_shift)).
|
83
|
+
"""
|
84
|
+
return vertical_shift + scale_factor * exp(growth_rate * (distance - horizontal_shift))
|
85
|
+
|
86
|
+
def sigmoid(distance: float, vertical_shift=0, scale_factor=1, growth_rate=1, horizontal_shift=0) -> float:
|
87
|
+
"""
|
88
|
+
Sigmoid distribution function.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
distance (float): The distance parameter.
|
92
|
+
vertical_shift (float): The vertical shift parameter.
|
93
|
+
scale_factor (float): The scale factor parameter.
|
94
|
+
growth_rate (float): The growth rate parameter.
|
95
|
+
horizontal_shift (float): The horizontal shift parameter.
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
The result of the sigmoid equation: vertical_shift + scale_factor / (1 + exp(-growth_rate * (distance - horizontal_shift))).
|
99
|
+
"""
|
100
|
+
return vertical_shift + scale_factor / (1 + exp(-growth_rate*(distance - horizontal_shift)))
|
101
|
+
|
102
|
+
|
103
|
+
def sinusoidal(distance: float, amplitude: float, frequency: float, phase: float) -> float:
|
104
|
+
"""
|
105
|
+
Sinusoidal distribution function.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
distance (float): The distance parameter.
|
109
|
+
amplitude (float): The amplitude parameter.
|
110
|
+
frequency (float): The frequency parameter.
|
111
|
+
phase (float): The phase parameter.
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
The result of the sinusoidal equation: amplitude * sin(frequency * distance + phase).
|
115
|
+
"""
|
116
|
+
return amplitude * sin(frequency * distance + phase)
|
117
|
+
|
118
|
+
def gaussian(distance: float, amplitude: float, mean: float, std: float) -> float:
|
119
|
+
"""
|
120
|
+
Gaussian distribution function.
|
121
|
+
|
122
|
+
Args:
|
123
|
+
distance (float): The distance parameter.
|
124
|
+
amplitude (float): The amplitude parameter.
|
125
|
+
mean (float): The mean parameter.
|
126
|
+
std (float): The standard deviation parameter.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
The result of the gaussian equation: amplitude * exp(-((distance - mean) ** 2) / (2 * std ** 2)).
|
130
|
+
"""
|
131
|
+
return amplitude * exp(-((distance - mean) ** 2) / (2 * std ** 2))
|
132
|
+
|
133
|
+
|
134
|
+
def step(distance: float, max_value: float, min_value: float, start: float, end: float) -> float:
|
135
|
+
"""
|
136
|
+
Step distribution function.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
distance (float): The distance parameter.
|
140
|
+
min_value (float): The minimum value parameter.
|
141
|
+
max_value (float): The maximum value parameter.
|
142
|
+
start (float): The start parameter.
|
143
|
+
end (float): The end parameter.
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
The result of the step equation: min_value if distance < start, max_value if distance > end, and a linear interpolation between min_value and max_value if start <= distance <= end.
|
147
|
+
"""
|
148
|
+
if start < distance < end:
|
149
|
+
return max_value
|
150
|
+
else:
|
151
|
+
return min_value
|
152
|
+
|
153
|
+
def polynomial(distance: float, coeffs: List[float]) -> float:
|
154
|
+
"""
|
155
|
+
Polynomial distribution function.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
distance (float): The distance parameter.
|
159
|
+
coefficients (List[float]): The coefficients of the polynomial.
|
160
|
+
|
161
|
+
Returns:
|
162
|
+
The result of the polynomial equation: sum(coefficients[i] * distance ** i for i in range(len(coefficients))).
|
163
|
+
"""
|
164
|
+
return polyval(coeffs, distance)
|
165
|
+
|
166
|
+
# aka ParametrizedFunction
|
167
|
+
class Distribution:
|
168
|
+
"""
|
169
|
+
A callable class for creating and managing distribution functions.
|
170
|
+
|
171
|
+
Parameters
|
172
|
+
----------
|
173
|
+
function_name : str
|
174
|
+
The name of the function to use.
|
175
|
+
\**parameters
|
176
|
+
The parameters to use for the function.
|
177
|
+
|
178
|
+
Attributes
|
179
|
+
----------
|
180
|
+
function : Callable
|
181
|
+
The function to use for evaluation.
|
182
|
+
parameters : dict
|
183
|
+
The parameters to use for the function.
|
184
|
+
|
185
|
+
Examples
|
186
|
+
--------
|
187
|
+
|
188
|
+
>>> func = Distribution('uniform', value=0)
|
189
|
+
>>> func(5)
|
190
|
+
0
|
191
|
+
"""
|
192
|
+
|
193
|
+
FUNCTIONS = {
|
194
|
+
'constant': {'func': constant, 'defaults': {'value': 0}},
|
195
|
+
'uniform': {'func': uniform, 'defaults': {'value': 0}},
|
196
|
+
'linear': {'func': linear, 'defaults': {'slope': 1, 'intercept': 0}},
|
197
|
+
'exponential': {'func': exponential, 'defaults': {'vertical_shift': 0, 'scale_factor': 1, 'growth_rate': 1, 'horizontal_shift': 0}},
|
198
|
+
'sigmoid': {'func': sigmoid, 'defaults': {'vertical_shift': 0, 'scale_factor': 1, 'growth_rate': 1, 'horizontal_shift': 0}},
|
199
|
+
'sinusoidal': {'func': sinusoidal, 'defaults': {'amplitude': 1, 'frequency': 1, 'phase': 0}},
|
200
|
+
'gaussian': {'func': gaussian, 'defaults': {'amplitude': 1, 'mean': 0, 'std': 1}},
|
201
|
+
'step': {'func': step, 'defaults': {'max_value': 1, 'min_value': 0, 'start': 0, 'end': 1}},
|
202
|
+
'polynomial': {'func': polynomial, 'defaults': {'coeffs': [1, 0]}},
|
203
|
+
|
204
|
+
}
|
205
|
+
|
206
|
+
@staticmethod
|
207
|
+
def from_dict(data: Dict[str, any]) -> 'Distribution':
|
208
|
+
"""
|
209
|
+
Creates a new Distribution from a dictionary.
|
210
|
+
|
211
|
+
Parameters
|
212
|
+
----------
|
213
|
+
data : dict
|
214
|
+
The dictionary containing the function data.
|
215
|
+
|
216
|
+
Returns
|
217
|
+
-------
|
218
|
+
Distribution
|
219
|
+
The new Distribution instance.
|
220
|
+
"""
|
221
|
+
return Distribution(data['function'], **data['parameters'])
|
222
|
+
|
223
|
+
def __init__(self, function_name: str, **parameters: Dict[str, float]) -> None:
|
224
|
+
"""
|
225
|
+
Creates a new parameterized function.
|
226
|
+
|
227
|
+
Parameters
|
228
|
+
----------
|
229
|
+
function_name : str
|
230
|
+
The name of the function to use.
|
231
|
+
\**parameters
|
232
|
+
The parameters to use for the function.
|
233
|
+
"""
|
234
|
+
func_data = self.FUNCTIONS[function_name]
|
235
|
+
self.function = func_data['func']
|
236
|
+
# Merge defaults with user parameters
|
237
|
+
valid_params = {k: v for k, v in parameters.items()
|
238
|
+
if k in func_data['defaults']}
|
239
|
+
self.parameters = {**func_data['defaults'], **valid_params}
|
240
|
+
|
241
|
+
def __repr__(self):
|
242
|
+
"""
|
243
|
+
Returns a string representation of the function.
|
244
|
+
|
245
|
+
Returns
|
246
|
+
-------
|
247
|
+
str
|
248
|
+
The string representation of the function.
|
249
|
+
"""
|
250
|
+
return f'{self.function.__name__}({self.parameters})'
|
251
|
+
|
252
|
+
def __call__(self, position):
|
253
|
+
"""
|
254
|
+
Calls the function with a given position.
|
255
|
+
|
256
|
+
Parameters
|
257
|
+
----------
|
258
|
+
position : float or numpy.ndarray
|
259
|
+
The position at which to evaluate the function.
|
260
|
+
|
261
|
+
Returns
|
262
|
+
-------
|
263
|
+
float or numpy.ndarray
|
264
|
+
The value of the function at the given position.
|
265
|
+
"""
|
266
|
+
return self.function(position, **self.parameters)
|
267
|
+
|
268
|
+
@property
|
269
|
+
def function_name(self):
|
270
|
+
"""
|
271
|
+
Returns the name of the function.
|
272
|
+
|
273
|
+
Returns
|
274
|
+
-------
|
275
|
+
str
|
276
|
+
The name of the function.
|
277
|
+
"""
|
278
|
+
return self.function.__name__
|
279
|
+
|
280
|
+
@property
|
281
|
+
def degree(self):
|
282
|
+
"""
|
283
|
+
Returns the degree of the polynomial function (if applicable).
|
284
|
+
|
285
|
+
Returns
|
286
|
+
-------
|
287
|
+
int
|
288
|
+
The degree of the function.
|
289
|
+
"""
|
290
|
+
if self.function_name == 'polynomial':
|
291
|
+
return len(self.parameters['coeffs']) - 1
|
292
|
+
else:
|
293
|
+
return None
|
294
|
+
|
295
|
+
def update_parameters(self, **new_params):
|
296
|
+
"""
|
297
|
+
Updates the parameters of the function.
|
298
|
+
|
299
|
+
Parameters
|
300
|
+
----------
|
301
|
+
\**new_params
|
302
|
+
The new parameters to update the function with.
|
303
|
+
"""
|
304
|
+
valid_params = {k: v for k, v in new_params.items()
|
305
|
+
if k in self.parameters}
|
306
|
+
# if any of the new parameters are invalid, raise an error
|
307
|
+
if len(valid_params) != len(new_params):
|
308
|
+
invalid_params = set(new_params) - set(valid_params)
|
309
|
+
warnings.warn(f'\nIgnoring invalid parameters: {invalid_params}.\nSupported parameters: {list(self.parameters.keys())}')
|
310
|
+
self.parameters.update(valid_params)
|
311
|
+
|
312
|
+
def to_dict(self):
|
313
|
+
"""
|
314
|
+
Exports the function to a dictionary format.
|
315
|
+
|
316
|
+
Returns
|
317
|
+
-------
|
318
|
+
dict
|
319
|
+
A dictionary representation of the function.
|
320
|
+
"""
|
321
|
+
return {
|
322
|
+
'function': self.function.__name__,
|
323
|
+
'parameters': self.parameters
|
324
|
+
}
|
@@ -0,0 +1,103 @@
|
|
1
|
+
from typing import List, Callable, Dict
|
2
|
+
|
3
|
+
from dendrotweaks.morphology.trees import Node
|
4
|
+
from dendrotweaks.morphology.sec_trees import Section
|
5
|
+
from dendrotweaks.morphology.seg_trees import Segment
|
6
|
+
from dendrotweaks.membrane.mechanisms import Mechanism
|
7
|
+
from dendrotweaks.membrane.distributions import Distribution
|
8
|
+
from dendrotweaks.utils import timeit
|
9
|
+
from dataclasses import dataclass, field, asdict
|
10
|
+
from typing import List, Tuple, Dict, Optional
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class SegmentGroup:
|
15
|
+
"""
|
16
|
+
A group of segments that share a common property.
|
17
|
+
|
18
|
+
Parameters
|
19
|
+
----------
|
20
|
+
name : str
|
21
|
+
The name of the segment group.
|
22
|
+
domains : List[str]
|
23
|
+
The domains the segments belong to.
|
24
|
+
select_by : Optional[str]
|
25
|
+
The property to select the segments by. Can be:
|
26
|
+
- 'diam': the diameter of the segment.
|
27
|
+
- 'section_diam': the diameter of the section the segment belongs to.
|
28
|
+
- 'distance': the distance of the segment from the root.
|
29
|
+
- 'domain_distance': the distance of the segment from the root within the domain.
|
30
|
+
min_value : Optional[float]
|
31
|
+
The minimum value of the property.
|
32
|
+
max_value : Optional[float]
|
33
|
+
The maximum value of the property.
|
34
|
+
|
35
|
+
Examples
|
36
|
+
--------
|
37
|
+
Create a segment group that selects segments by diameter:
|
38
|
+
|
39
|
+
>>> group = SegmentGroup('group1', domains=['dend'], select_by='diam', min_value=1, max_value=2)
|
40
|
+
>>> group
|
41
|
+
SegmentGroup("group1", domains=['dend'], diam(1, 2))
|
42
|
+
|
43
|
+
Check if a segment is in the group:
|
44
|
+
|
45
|
+
>>> segment in group
|
46
|
+
True
|
47
|
+
"""
|
48
|
+
name: str
|
49
|
+
domains: List[str]
|
50
|
+
select_by: Optional[str] = None
|
51
|
+
min_value: Optional[float] = None
|
52
|
+
max_value: Optional[float] = None
|
53
|
+
|
54
|
+
def _get_segment_value(self, segment) -> Optional[float]:
|
55
|
+
if self.select_by == 'diam':
|
56
|
+
return segment.diam
|
57
|
+
elif self.select_by == 'section_diam':
|
58
|
+
return segment._section._ref.diam
|
59
|
+
elif self.select_by == 'distance':
|
60
|
+
return segment.path_distance()
|
61
|
+
elif self.select_by == 'domain_distance':
|
62
|
+
return segment.path_distance(within_domain=True)
|
63
|
+
return None
|
64
|
+
|
65
|
+
def __contains__(self, segment) -> bool:
|
66
|
+
if segment.domain not in self.domains:
|
67
|
+
return False
|
68
|
+
if self.select_by is None:
|
69
|
+
return True
|
70
|
+
|
71
|
+
segment_value = self._get_segment_value(segment)
|
72
|
+
return (
|
73
|
+
(self.min_value is None or segment_value > self.min_value) and
|
74
|
+
(self.max_value is None or segment_value <= self.max_value)
|
75
|
+
)
|
76
|
+
|
77
|
+
def __repr__(self):
|
78
|
+
filters = (
|
79
|
+
f"{self.select_by}({self.min_value}, {self.max_value})"
|
80
|
+
if self.select_by is not None and (self.min_value is not None or self.max_value is not None) else ""
|
81
|
+
)
|
82
|
+
return f'SegmentGroup("{self.name}", domains={self.domains}' + (f", {filters}" if filters else "") + ')'
|
83
|
+
|
84
|
+
def to_dict(self) -> Dict:
|
85
|
+
"""
|
86
|
+
Convert the SegmentGroup to a dictionary.
|
87
|
+
"""
|
88
|
+
result = {
|
89
|
+
'name': self.name,
|
90
|
+
'domains': self.domains,
|
91
|
+
'select_by': self.select_by,
|
92
|
+
'min_value': self.min_value,
|
93
|
+
'max_value': self.max_value,
|
94
|
+
}
|
95
|
+
return {k: v for k, v in result.items() if v is not None}
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
def from_dict(data: Dict) -> 'SegmentGroup':
|
99
|
+
"""
|
100
|
+
Create a SegmentGroup from a dictionary.
|
101
|
+
"""
|
102
|
+
return SegmentGroup(**data)
|
103
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from dendrotweaks.membrane.io.loader import MODFileLoader
|
2
|
+
from dendrotweaks.membrane.io.converter import MODFileConverter
|
3
|
+
|
4
|
+
from dendrotweaks.membrane.io.reader import MODFileReader
|
5
|
+
from dendrotweaks.membrane.io.parser import MODFileParser
|
6
|
+
from dendrotweaks.membrane.io.code_generators import PythonCodeGenerator
|
7
|
+
from dendrotweaks.membrane.io.code_generators import NMODLCodeGenerator
|
8
|
+
|
9
|
+
from dendrotweaks.membrane.io.factories import create_channel
|
10
|
+
from dendrotweaks.membrane.io.factories import create_standard_channel
|
11
|
+
from dendrotweaks.membrane.io.factories import standardize_channel
|