pytme 0.2.1__cp311-cp311-macosx_14_0_arm64.whl → 0.2.3__cp311-cp311-macosx_14_0_arm64.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.
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/match_template.py +219 -216
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/postprocess.py +86 -54
- pytme-0.2.3.data/scripts/preprocess.py +132 -0
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/preprocessor_gui.py +181 -94
- pytme-0.2.3.dist-info/METADATA +92 -0
- pytme-0.2.3.dist-info/RECORD +75 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/WHEEL +1 -1
- pytme-0.2.1.data/scripts/preprocess.py → scripts/eval.py +1 -1
- scripts/extract_candidates.py +20 -13
- scripts/match_template.py +219 -216
- scripts/match_template_filters.py +154 -95
- scripts/postprocess.py +86 -54
- scripts/preprocess.py +95 -56
- scripts/preprocessor_gui.py +181 -94
- scripts/refine_matches.py +265 -61
- tme/__init__.py +0 -1
- tme/__version__.py +1 -1
- tme/analyzer.py +458 -813
- tme/backends/__init__.py +40 -11
- tme/backends/_jax_utils.py +187 -0
- tme/backends/cupy_backend.py +109 -226
- tme/backends/jax_backend.py +230 -152
- tme/backends/matching_backend.py +445 -384
- tme/backends/mlx_backend.py +32 -59
- tme/backends/npfftw_backend.py +240 -507
- tme/backends/pytorch_backend.py +30 -151
- tme/density.py +248 -371
- tme/extensions.cpython-311-darwin.so +0 -0
- tme/matching_data.py +328 -284
- tme/matching_exhaustive.py +195 -1499
- tme/matching_optimization.py +143 -106
- tme/matching_scores.py +887 -0
- tme/matching_utils.py +287 -388
- tme/memory.py +377 -0
- tme/orientations.py +78 -21
- tme/parser.py +3 -4
- tme/preprocessing/_utils.py +61 -32
- tme/preprocessing/composable_filter.py +7 -4
- tme/preprocessing/compose.py +7 -3
- tme/preprocessing/frequency_filters.py +49 -39
- tme/preprocessing/tilt_series.py +44 -72
- tme/preprocessor.py +560 -526
- tme/structure.py +491 -188
- tme/types.py +5 -3
- pytme-0.2.1.dist-info/METADATA +0 -73
- pytme-0.2.1.dist-info/RECORD +0 -73
- tme/helpers.py +0 -881
- tme/matching_constrained.py +0 -195
- {pytme-0.2.1.data → pytme-0.2.3.data}/scripts/estimate_ram_usage.py +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/LICENSE +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/entry_points.txt +0 -0
- {pytme-0.2.1.dist-info → pytme-0.2.3.dist-info}/top_level.txt +0 -0
tme/memory.py
ADDED
@@ -0,0 +1,377 @@
|
|
1
|
+
""" Compute memory consumption of template matching components.
|
2
|
+
|
3
|
+
Copyright (c) 2023 European Molecular Biology Laboratory
|
4
|
+
|
5
|
+
Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
|
6
|
+
"""
|
7
|
+
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
from typing import Tuple
|
10
|
+
|
11
|
+
import numpy as np
|
12
|
+
from pyfftw import next_fast_len
|
13
|
+
|
14
|
+
|
15
|
+
class MatchingMemoryUsage(ABC):
|
16
|
+
"""
|
17
|
+
Class specification for estimating the memory requirements of template matching.
|
18
|
+
|
19
|
+
Parameters
|
20
|
+
----------
|
21
|
+
fast_shape : tuple of int
|
22
|
+
Shape of the real array.
|
23
|
+
ft_shape : tuple of int
|
24
|
+
Shape of the complex array.
|
25
|
+
float_nbytes : int
|
26
|
+
Number of bytes of the used float, e.g. 4 for float32.
|
27
|
+
complex_nbytes : int
|
28
|
+
Number of bytes of the used complex, e.g. 8 for complex64.
|
29
|
+
integer_nbytes : int
|
30
|
+
Number of bytes of the used integer, e.g. 4 for int32.
|
31
|
+
|
32
|
+
Attributes
|
33
|
+
----------
|
34
|
+
real_array_size : int
|
35
|
+
Number of elements in real array.
|
36
|
+
complex_array_size : int
|
37
|
+
Number of elements in complex array.
|
38
|
+
float_nbytes : int
|
39
|
+
Number of bytes of the used float, e.g. 4 for float32.
|
40
|
+
complex_nbytes : int
|
41
|
+
Number of bytes of the used complex, e.g. 8 for complex64.
|
42
|
+
integer_nbytes : int
|
43
|
+
Number of bytes of the used integer, e.g. 4 for int32.
|
44
|
+
|
45
|
+
Methods
|
46
|
+
-------
|
47
|
+
base_usage():
|
48
|
+
Returns the base memory usage in bytes.
|
49
|
+
per_fork():
|
50
|
+
Returns the memory usage in bytes per fork.
|
51
|
+
"""
|
52
|
+
|
53
|
+
def __init__(
|
54
|
+
self,
|
55
|
+
fast_shape: Tuple[int],
|
56
|
+
ft_shape: Tuple[int],
|
57
|
+
float_nbytes: int,
|
58
|
+
complex_nbytes: int,
|
59
|
+
integer_nbytes: int,
|
60
|
+
):
|
61
|
+
self.real_array_size = np.prod(fast_shape)
|
62
|
+
self.complex_array_size = np.prod(ft_shape)
|
63
|
+
self.float_nbytes = float_nbytes
|
64
|
+
self.complex_nbytes = complex_nbytes
|
65
|
+
self.integer_nbytes = integer_nbytes
|
66
|
+
|
67
|
+
@abstractmethod
|
68
|
+
def base_usage(self) -> int:
|
69
|
+
"""Return the base memory usage in bytes."""
|
70
|
+
|
71
|
+
@abstractmethod
|
72
|
+
def per_fork(self) -> int:
|
73
|
+
"""Return the memory usage per fork in bytes."""
|
74
|
+
|
75
|
+
|
76
|
+
class CCMemoryUsage(MatchingMemoryUsage):
|
77
|
+
"""
|
78
|
+
Memory usage estimation for CC scoring.
|
79
|
+
|
80
|
+
See Also
|
81
|
+
--------
|
82
|
+
:py:meth:`tme.matching_exhaustive.cc_setup`.
|
83
|
+
"""
|
84
|
+
|
85
|
+
def base_usage(self) -> int:
|
86
|
+
float_arrays = self.real_array_size * self.float_nbytes
|
87
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes
|
88
|
+
return float_arrays + complex_arrays
|
89
|
+
|
90
|
+
def per_fork(self) -> int:
|
91
|
+
float_arrays = self.real_array_size * self.float_nbytes
|
92
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes
|
93
|
+
return float_arrays + complex_arrays
|
94
|
+
|
95
|
+
|
96
|
+
class LCCMemoryUsage(CCMemoryUsage):
|
97
|
+
"""
|
98
|
+
Memory usage estimation for LCC scoring.
|
99
|
+
See Also
|
100
|
+
--------
|
101
|
+
:py:meth:`tme.matching_exhaustive.lcc_setup`.
|
102
|
+
"""
|
103
|
+
|
104
|
+
|
105
|
+
class CORRMemoryUsage(MatchingMemoryUsage):
|
106
|
+
"""
|
107
|
+
Memory usage estimation for CORR scoring.
|
108
|
+
|
109
|
+
See Also
|
110
|
+
--------
|
111
|
+
:py:meth:`tme.matching_exhaustive.corr_setup`.
|
112
|
+
"""
|
113
|
+
|
114
|
+
def base_usage(self) -> int:
|
115
|
+
float_arrays = self.real_array_size * self.float_nbytes * 4
|
116
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes
|
117
|
+
return float_arrays + complex_arrays
|
118
|
+
|
119
|
+
def per_fork(self) -> int:
|
120
|
+
float_arrays = self.real_array_size * self.float_nbytes
|
121
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes
|
122
|
+
return float_arrays + complex_arrays
|
123
|
+
|
124
|
+
|
125
|
+
class CAMMemoryUsage(CORRMemoryUsage):
|
126
|
+
"""
|
127
|
+
Memory usage estimation for CAM scoring.
|
128
|
+
|
129
|
+
See Also
|
130
|
+
--------
|
131
|
+
:py:meth:`tme.matching_exhaustive.cam_setup`.
|
132
|
+
"""
|
133
|
+
|
134
|
+
|
135
|
+
class FLCSphericalMaskMemoryUsage(CORRMemoryUsage):
|
136
|
+
"""
|
137
|
+
Memory usage estimation for FLCMSphericalMask scoring.
|
138
|
+
|
139
|
+
See Also
|
140
|
+
--------
|
141
|
+
:py:meth:`tme.matching_exhaustive.flcSphericalMask_setup`.
|
142
|
+
"""
|
143
|
+
|
144
|
+
|
145
|
+
class FLCMemoryUsage(MatchingMemoryUsage):
|
146
|
+
"""
|
147
|
+
Memory usage estimation for FLC scoring.
|
148
|
+
|
149
|
+
See Also
|
150
|
+
--------
|
151
|
+
:py:meth:`tme.matching_exhaustive.flc_setup`.
|
152
|
+
"""
|
153
|
+
|
154
|
+
def base_usage(self) -> int:
|
155
|
+
float_arrays = self.real_array_size * self.float_nbytes * 2
|
156
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes * 2
|
157
|
+
return float_arrays + complex_arrays
|
158
|
+
|
159
|
+
def per_fork(self) -> int:
|
160
|
+
float_arrays = self.real_array_size * self.float_nbytes * 3
|
161
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes * 2
|
162
|
+
return float_arrays + complex_arrays
|
163
|
+
|
164
|
+
|
165
|
+
class MCCMemoryUsage(MatchingMemoryUsage):
|
166
|
+
"""
|
167
|
+
Memory usage estimation for MCC scoring.
|
168
|
+
|
169
|
+
See Also
|
170
|
+
--------
|
171
|
+
:py:meth:`tme.matching_exhaustive.mcc_setup`.
|
172
|
+
"""
|
173
|
+
|
174
|
+
def base_usage(self) -> int:
|
175
|
+
float_arrays = self.real_array_size * self.float_nbytes * 2
|
176
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes * 3
|
177
|
+
return float_arrays + complex_arrays
|
178
|
+
|
179
|
+
def per_fork(self) -> int:
|
180
|
+
float_arrays = self.real_array_size * self.float_nbytes * 6
|
181
|
+
complex_arrays = self.complex_array_size * self.complex_nbytes
|
182
|
+
return float_arrays + complex_arrays
|
183
|
+
|
184
|
+
|
185
|
+
class MaxScoreOverRotationsMemoryUsage(MatchingMemoryUsage):
|
186
|
+
"""
|
187
|
+
Memory usage estimation MaxScoreOverRotations Analyzer.
|
188
|
+
|
189
|
+
See Also
|
190
|
+
--------
|
191
|
+
:py:class:`tme.analyzer.MaxScoreOverRotations`.
|
192
|
+
"""
|
193
|
+
|
194
|
+
def base_usage(self) -> int:
|
195
|
+
float_arrays = self.real_array_size * self.float_nbytes * 2
|
196
|
+
return float_arrays
|
197
|
+
|
198
|
+
def per_fork(self) -> int:
|
199
|
+
return 0
|
200
|
+
|
201
|
+
|
202
|
+
class PeakCallerMaximumFilterMemoryUsage(MatchingMemoryUsage):
|
203
|
+
"""
|
204
|
+
Memory usage estimation MaxScoreOverRotations Analyzer.
|
205
|
+
|
206
|
+
See Also
|
207
|
+
--------
|
208
|
+
:py:class:`tme.analyzer.PeakCallerMaximumFilter`.
|
209
|
+
"""
|
210
|
+
|
211
|
+
def base_usage(self) -> int:
|
212
|
+
float_arrays = self.real_array_size * self.float_nbytes
|
213
|
+
return float_arrays
|
214
|
+
|
215
|
+
def per_fork(self) -> int:
|
216
|
+
float_arrays = self.real_array_size * self.float_nbytes
|
217
|
+
return float_arrays
|
218
|
+
|
219
|
+
|
220
|
+
class CupyBackendMemoryUsage(MatchingMemoryUsage):
|
221
|
+
"""
|
222
|
+
Memory usage estimation for CupyBackend.
|
223
|
+
|
224
|
+
See Also
|
225
|
+
--------
|
226
|
+
:py:class:`tme.backends.CupyBackend`.
|
227
|
+
"""
|
228
|
+
|
229
|
+
def base_usage(self) -> int:
|
230
|
+
# FFT plans, overhead from assigning FFT result, rotation interpolation
|
231
|
+
complex_arrays = self.real_array_size * self.complex_nbytes * 3
|
232
|
+
float_arrays = self.complex_array_size * self.float_nbytes * 2
|
233
|
+
return float_arrays + complex_arrays
|
234
|
+
|
235
|
+
def per_fork(self) -> int:
|
236
|
+
return 0
|
237
|
+
|
238
|
+
|
239
|
+
def _compute_convolution_shapes(
|
240
|
+
arr1_shape: Tuple[int], arr2_shape: Tuple[int]
|
241
|
+
) -> Tuple[Tuple[int], Tuple[int], Tuple[int]]:
|
242
|
+
"""
|
243
|
+
Computes regular, optimized and fourier convolution shape.
|
244
|
+
|
245
|
+
Parameters
|
246
|
+
----------
|
247
|
+
arr1_shape : tuple
|
248
|
+
Tuple of integers corresponding to array1 shape.
|
249
|
+
arr2_shape : tuple
|
250
|
+
Tuple of integers corresponding to array2 shape.
|
251
|
+
|
252
|
+
Returns
|
253
|
+
-------
|
254
|
+
tuple
|
255
|
+
Tuple with regular convolution shape, convolution shape optimized for faster
|
256
|
+
fourier transform, shape of the forward fourier transform
|
257
|
+
(see :py:meth:`build_fft`).
|
258
|
+
"""
|
259
|
+
convolution_shape = np.add(arr1_shape, arr2_shape) - 1
|
260
|
+
fast_shape = [next_fast_len(x) for x in convolution_shape]
|
261
|
+
fast_ft_shape = list(fast_shape[:-1]) + [fast_shape[-1] // 2 + 1]
|
262
|
+
|
263
|
+
return convolution_shape, fast_shape, fast_ft_shape
|
264
|
+
|
265
|
+
|
266
|
+
MATCHING_MEMORY_REGISTRY = {
|
267
|
+
"CC": CCMemoryUsage,
|
268
|
+
"LCC": LCCMemoryUsage,
|
269
|
+
"CORR": CORRMemoryUsage,
|
270
|
+
"CAM": CAMMemoryUsage,
|
271
|
+
"MCC": MCCMemoryUsage,
|
272
|
+
"FLCSphericalMask": FLCSphericalMaskMemoryUsage,
|
273
|
+
"FLC": FLCMemoryUsage,
|
274
|
+
"MaxScoreOverRotations": MaxScoreOverRotationsMemoryUsage,
|
275
|
+
"PeakCallerMaximumFilter": PeakCallerMaximumFilterMemoryUsage,
|
276
|
+
"cupy": CupyBackendMemoryUsage,
|
277
|
+
"pytorch": CupyBackendMemoryUsage,
|
278
|
+
}
|
279
|
+
|
280
|
+
|
281
|
+
def estimate_ram_usage(
|
282
|
+
shape1: Tuple[int],
|
283
|
+
shape2: Tuple[int],
|
284
|
+
matching_method: str,
|
285
|
+
ncores: int,
|
286
|
+
analyzer_method: str = None,
|
287
|
+
backend: str = None,
|
288
|
+
float_nbytes: int = 4,
|
289
|
+
complex_nbytes: int = 8,
|
290
|
+
integer_nbytes: int = 4,
|
291
|
+
) -> int:
|
292
|
+
"""
|
293
|
+
Estimate the RAM usage for a given convolution operation based on input shapes,
|
294
|
+
matching_method, and number of cores.
|
295
|
+
|
296
|
+
Parameters
|
297
|
+
----------
|
298
|
+
shape1 : tuple
|
299
|
+
The shape of the input target.
|
300
|
+
shape2 : tuple
|
301
|
+
The shape of the input template.
|
302
|
+
matching_method : str
|
303
|
+
The method used for the operation.
|
304
|
+
is_gpu : bool, optional
|
305
|
+
Whether the computation is performed on GPU. This factors in FFT
|
306
|
+
plan caching.
|
307
|
+
analyzer_method : str, optional
|
308
|
+
The method used for score analysis.
|
309
|
+
backend : str, optional
|
310
|
+
Backend used for computation.
|
311
|
+
ncores : int
|
312
|
+
The number of CPU cores used for the operation.
|
313
|
+
float_nbytes : int
|
314
|
+
Number of bytes of the used float, e.g. 4 for float32.
|
315
|
+
complex_nbytes : int
|
316
|
+
Number of bytes of the used complex, e.g. 8 for complex64.
|
317
|
+
integer_nbytes : int
|
318
|
+
Number of bytes of the used integer, e.g. 4 for int32.
|
319
|
+
|
320
|
+
Returns
|
321
|
+
-------
|
322
|
+
int
|
323
|
+
The estimated RAM usage for the operation in bytes.
|
324
|
+
|
325
|
+
Notes
|
326
|
+
-----
|
327
|
+
Residual memory from other objects that may remain allocated during
|
328
|
+
template matching, e.g. the full sized target when using splitting,
|
329
|
+
are not considered by this function.
|
330
|
+
|
331
|
+
Raises
|
332
|
+
------
|
333
|
+
ValueError
|
334
|
+
If an unsupported matching_methode is provided.
|
335
|
+
"""
|
336
|
+
if matching_method not in MATCHING_MEMORY_REGISTRY:
|
337
|
+
raise ValueError(
|
338
|
+
f"Supported options are {','.join(MATCHING_MEMORY_REGISTRY.keys())}"
|
339
|
+
)
|
340
|
+
|
341
|
+
convolution_shape, fast_shape, ft_shape = _compute_convolution_shapes(
|
342
|
+
shape1, shape2
|
343
|
+
)
|
344
|
+
|
345
|
+
memory_instance = MATCHING_MEMORY_REGISTRY[matching_method](
|
346
|
+
fast_shape=fast_shape,
|
347
|
+
ft_shape=ft_shape,
|
348
|
+
float_nbytes=float_nbytes,
|
349
|
+
complex_nbytes=complex_nbytes,
|
350
|
+
integer_nbytes=integer_nbytes,
|
351
|
+
)
|
352
|
+
|
353
|
+
nbytes = memory_instance.base_usage() + memory_instance.per_fork() * ncores
|
354
|
+
|
355
|
+
analyzer_instance = MATCHING_MEMORY_REGISTRY.get(analyzer_method, None)
|
356
|
+
if analyzer_instance is not None:
|
357
|
+
analyzer_instance = analyzer_instance(
|
358
|
+
fast_shape=fast_shape,
|
359
|
+
ft_shape=ft_shape,
|
360
|
+
float_nbytes=float_nbytes,
|
361
|
+
complex_nbytes=complex_nbytes,
|
362
|
+
integer_nbytes=integer_nbytes,
|
363
|
+
)
|
364
|
+
nbytes += analyzer_instance.base_usage() + analyzer_instance.per_fork() * ncores
|
365
|
+
|
366
|
+
backend_instance = MATCHING_MEMORY_REGISTRY.get(backend, None)
|
367
|
+
if backend_instance is not None:
|
368
|
+
backend_instance = backend_instance(
|
369
|
+
fast_shape=fast_shape,
|
370
|
+
ft_shape=ft_shape,
|
371
|
+
float_nbytes=float_nbytes,
|
372
|
+
complex_nbytes=complex_nbytes,
|
373
|
+
integer_nbytes=integer_nbytes,
|
374
|
+
)
|
375
|
+
nbytes += backend_instance.base_usage() + backend_instance.per_fork() * ncores
|
376
|
+
|
377
|
+
return nbytes
|
tme/orientations.py
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
|
7
7
|
"""
|
8
8
|
import re
|
9
|
+
import warnings
|
9
10
|
from collections import deque
|
10
11
|
from dataclasses import dataclass
|
11
12
|
from string import ascii_lowercase
|
@@ -62,16 +63,16 @@ class Orientations:
|
|
62
63
|
Array with additional orientation details (n, ).
|
63
64
|
"""
|
64
65
|
|
65
|
-
#:
|
66
|
+
#: Array with translations of each orientation (n, d).
|
66
67
|
translations: np.ndarray
|
67
68
|
|
68
|
-
#:
|
69
|
+
#: Array with zyx euler angles of each orientation (n, d).
|
69
70
|
rotations: np.ndarray
|
70
71
|
|
71
|
-
#:
|
72
|
+
#: Array with scores of each orientation (n, ).
|
72
73
|
scores: np.ndarray
|
73
74
|
|
74
|
-
#:
|
75
|
+
#: Array with additional details of each orientation(n, ).
|
75
76
|
details: np.ndarray
|
76
77
|
|
77
78
|
def __post_init__(self):
|
@@ -130,9 +131,21 @@ class Orientations:
|
|
130
131
|
"scores",
|
131
132
|
"details",
|
132
133
|
)
|
133
|
-
kwargs = {attr: getattr(self, attr)[indices] for attr in attributes}
|
134
|
+
kwargs = {attr: getattr(self, attr)[indices].copy() for attr in attributes}
|
134
135
|
return self.__class__(**kwargs)
|
135
136
|
|
137
|
+
def copy(self) -> "Orientations":
|
138
|
+
"""
|
139
|
+
Create a copy of the current class instance.
|
140
|
+
|
141
|
+
Returns
|
142
|
+
-------
|
143
|
+
:py:class:`Orientations`
|
144
|
+
Copy of the class instance.
|
145
|
+
"""
|
146
|
+
indices = np.arange(self.scores.size)
|
147
|
+
return self[indices]
|
148
|
+
|
136
149
|
def to_file(self, filename: str, file_format: type = None, **kwargs) -> None:
|
137
150
|
"""
|
138
151
|
Save the current class instance to a file in the specified format.
|
@@ -146,7 +159,7 @@ class Orientations:
|
|
146
159
|
the file_format from the typical extension. Supported formats are
|
147
160
|
|
148
161
|
+---------------+----------------------------------------------------+
|
149
|
-
| text |
|
162
|
+
| text | pytme's standard tab-separated orientations file |
|
150
163
|
+---------------+----------------------------------------------------+
|
151
164
|
| relion | Creates a STAR file of orientations |
|
152
165
|
+---------------+----------------------------------------------------+
|
@@ -207,11 +220,11 @@ class Orientations:
|
|
207
220
|
with open(filename, mode="w", encoding="utf-8") as ofile:
|
208
221
|
_ = ofile.write(f"{header}\n")
|
209
222
|
for translation, angles, score, detail in self:
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
f"{translation_string}\t{angle_string}\t{score}\t{detail}\n"
|
223
|
+
out_string = (
|
224
|
+
"\t".join([str(x) for x in (*translation, *angles, score, detail)])
|
225
|
+
+ "\n"
|
214
226
|
)
|
227
|
+
_ = ofile.write(out_string)
|
215
228
|
return None
|
216
229
|
|
217
230
|
def _to_dynamo_tbl(
|
@@ -289,7 +302,7 @@ class Orientations:
|
|
289
302
|
def _to_relion_star(
|
290
303
|
self,
|
291
304
|
filename: str,
|
292
|
-
|
305
|
+
name: str = None,
|
293
306
|
ctf_image: str = None,
|
294
307
|
sampling_rate: float = 1.0,
|
295
308
|
subtomogram_size: int = 0,
|
@@ -301,8 +314,9 @@ class Orientations:
|
|
301
314
|
----------
|
302
315
|
filename : str
|
303
316
|
The name of the file to save the orientations.
|
304
|
-
|
305
|
-
|
317
|
+
name : str or list of str, optional
|
318
|
+
Path to image file the orientation is in reference to. If name is a list
|
319
|
+
its assumed to correspond to _rlnImageName, otherwise _rlnMicrographName.
|
306
320
|
ctf_image : str, optional
|
307
321
|
Path to CTF or wedge mask RELION.
|
308
322
|
sampling_rate : float, optional
|
@@ -340,6 +354,21 @@ class Orientations:
|
|
340
354
|
optics_header = "\n".join(optics_header)
|
341
355
|
optics_data = "\t".join(optics_data)
|
342
356
|
|
357
|
+
if name is None:
|
358
|
+
name = ""
|
359
|
+
warnings.warn(
|
360
|
+
"Consider specifying the name argument. A single string will be "
|
361
|
+
"interpreted as path to the original micrograph, a list of strings "
|
362
|
+
"as path to individual subsets."
|
363
|
+
)
|
364
|
+
|
365
|
+
name_reference = "_rlnImageName"
|
366
|
+
if isinstance(name, str):
|
367
|
+
name = [
|
368
|
+
name,
|
369
|
+
] * self.translations.shape[0]
|
370
|
+
name_reference = "_rlnMicrographName"
|
371
|
+
|
343
372
|
header = [
|
344
373
|
"data_particles",
|
345
374
|
"",
|
@@ -347,7 +376,7 @@ class Orientations:
|
|
347
376
|
"_rlnCoordinateX",
|
348
377
|
"_rlnCoordinateY",
|
349
378
|
"_rlnCoordinateZ",
|
350
|
-
|
379
|
+
name_reference,
|
351
380
|
"_rlnAngleRot",
|
352
381
|
"_rlnAngleTilt",
|
353
382
|
"_rlnAnglePsi",
|
@@ -359,8 +388,6 @@ class Orientations:
|
|
359
388
|
ctf_image = "" if ctf_image is None else f"\t{ctf_image}"
|
360
389
|
|
361
390
|
header = "\n".join(header)
|
362
|
-
name_prefix = "" if name_prefix is None else name_prefix
|
363
|
-
|
364
391
|
with open(filename, mode="w", encoding="utf-8") as ofile:
|
365
392
|
_ = ofile.write(f"{optics_header}\n")
|
366
393
|
_ = ofile.write(f"{optics_data}\n")
|
@@ -375,9 +402,8 @@ class Orientations:
|
|
375
402
|
|
376
403
|
translation_string = "\t".join([str(x) for x in translation][::-1])
|
377
404
|
angle_string = "\t".join([str(x) for x in rotation])
|
378
|
-
name = f"{name_prefix}_{index}.mrc"
|
379
405
|
_ = ofile.write(
|
380
|
-
f"{translation_string}\t{name}\t{angle_string}\t1{ctf_image}\n"
|
406
|
+
f"{translation_string}\t{name[index]}\t{angle_string}\t1{ctf_image}\n"
|
381
407
|
)
|
382
408
|
|
383
409
|
return None
|
@@ -465,8 +491,10 @@ class Orientations:
|
|
465
491
|
|
466
492
|
Notes
|
467
493
|
-----
|
468
|
-
The text file is expected to have a header and data in columns
|
469
|
-
|
494
|
+
The text file is expected to have a header and data in columns. Colums containing
|
495
|
+
the name euler are considered to specify rotations. The second last and last
|
496
|
+
column correspond to score and detail. Its possible to only specify translations,
|
497
|
+
in this case the remaining columns will be filled with trivial values.
|
470
498
|
"""
|
471
499
|
with open(filename, mode="r", encoding="utf-8") as infile:
|
472
500
|
data = [x.strip().split("\t") for x in infile.read().split("\n")]
|
@@ -493,6 +521,32 @@ class Orientations:
|
|
493
521
|
score = np.array(score)
|
494
522
|
detail = np.array(detail)
|
495
523
|
|
524
|
+
if translation.shape[1] == len(header):
|
525
|
+
rotation = np.zeros(translation.shape, dtype=np.float32)
|
526
|
+
score = np.zeros(translation.shape[0], dtype=np.float32)
|
527
|
+
detail = np.zeros(translation.shape[0], dtype=np.float32) - 1
|
528
|
+
|
529
|
+
if rotation.size == 0 and translation.shape[0] != 0:
|
530
|
+
rotation = np.zeros(translation.shape, dtype=np.float32)
|
531
|
+
|
532
|
+
header_order = tuple(x for x in header if x in ascii_lowercase)
|
533
|
+
header_order = zip(header_order, range(len(header_order)))
|
534
|
+
sort_order = tuple(
|
535
|
+
x[1] for x in sorted(header_order, key=lambda x: x[0], reverse=True)
|
536
|
+
)
|
537
|
+
translation = translation[..., sort_order]
|
538
|
+
|
539
|
+
header_order = tuple(
|
540
|
+
x
|
541
|
+
for x in header
|
542
|
+
if "euler" in x and x.replace("euler_", "") in ascii_lowercase
|
543
|
+
)
|
544
|
+
header_order = zip(header_order, range(len(header_order)))
|
545
|
+
sort_order = tuple(
|
546
|
+
x[1] for x in sorted(header_order, key=lambda x: x[0], reverse=True)
|
547
|
+
)
|
548
|
+
rotation = rotation[..., sort_order]
|
549
|
+
|
496
550
|
return translation, rotation, score, detail
|
497
551
|
|
498
552
|
@staticmethod
|
@@ -544,7 +598,10 @@ class Orientations:
|
|
544
598
|
cls, filename: str, delimiter: str = None
|
545
599
|
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
546
600
|
ret = cls._parse_star(filename=filename, delimiter=delimiter)
|
547
|
-
|
601
|
+
|
602
|
+
ret = ret.get("data_particles", None)
|
603
|
+
if ret is None:
|
604
|
+
raise ValueError(f"No data_particles section found in {filename}.")
|
548
605
|
|
549
606
|
translation = np.vstack(
|
550
607
|
(ret["_rlnCoordinateZ"], ret["_rlnCoordinateY"], ret["_rlnCoordinateX"])
|
tme/parser.py
CHANGED
@@ -137,8 +137,7 @@ class Parser(ABC):
|
|
137
137
|
|
138
138
|
class PDBParser(Parser):
|
139
139
|
"""
|
140
|
-
|
141
|
-
This class is specifically designed to work with PDB file format.
|
140
|
+
Convert PDB file data into a dictionary representation [1]_.
|
142
141
|
|
143
142
|
References
|
144
143
|
----------
|
@@ -228,8 +227,8 @@ class PDBParser(Parser):
|
|
228
227
|
|
229
228
|
class MMCIFParser(Parser):
|
230
229
|
"""
|
231
|
-
|
232
|
-
|
230
|
+
Convert MMCIF file data into a dictionary representation. This implementation
|
231
|
+
heavily relies on the atomium library [1]_.
|
233
232
|
|
234
233
|
References
|
235
234
|
----------
|