pytme 0.3b0__cp311-cp311-macosx_15_0_arm64.whl → 0.3.1__cp311-cp311-macosx_15_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.
Files changed (73) hide show
  1. {pytme-0.3b0.data → pytme-0.3.1.data}/scripts/estimate_memory_usage.py +1 -5
  2. {pytme-0.3b0.data → pytme-0.3.1.data}/scripts/match_template.py +177 -226
  3. {pytme-0.3b0.data → pytme-0.3.1.data}/scripts/postprocess.py +69 -47
  4. {pytme-0.3b0.data → pytme-0.3.1.data}/scripts/preprocess.py +10 -23
  5. {pytme-0.3b0.data → pytme-0.3.1.data}/scripts/preprocessor_gui.py +98 -28
  6. pytme-0.3.1.data/scripts/pytme_runner.py +1223 -0
  7. {pytme-0.3b0.dist-info → pytme-0.3.1.dist-info}/METADATA +15 -15
  8. pytme-0.3.1.dist-info/RECORD +133 -0
  9. {pytme-0.3b0.dist-info → pytme-0.3.1.dist-info}/entry_points.txt +1 -0
  10. pytme-0.3.1.dist-info/licenses/LICENSE +339 -0
  11. scripts/estimate_memory_usage.py +1 -5
  12. scripts/eval.py +93 -0
  13. scripts/extract_candidates.py +118 -99
  14. scripts/match_template.py +177 -226
  15. scripts/match_template_filters.py +1200 -0
  16. scripts/postprocess.py +69 -47
  17. scripts/preprocess.py +10 -23
  18. scripts/preprocessor_gui.py +98 -28
  19. scripts/pytme_runner.py +1223 -0
  20. scripts/refine_matches.py +156 -387
  21. tests/data/.DS_Store +0 -0
  22. tests/data/Blurring/.DS_Store +0 -0
  23. tests/data/Maps/.DS_Store +0 -0
  24. tests/data/Raw/.DS_Store +0 -0
  25. tests/data/Structures/.DS_Store +0 -0
  26. tests/preprocessing/test_frequency_filters.py +19 -10
  27. tests/preprocessing/test_utils.py +18 -0
  28. tests/test_analyzer.py +122 -122
  29. tests/test_backends.py +4 -9
  30. tests/test_density.py +0 -1
  31. tests/test_matching_cli.py +30 -30
  32. tests/test_matching_data.py +5 -5
  33. tests/test_matching_utils.py +11 -61
  34. tests/test_rotations.py +1 -1
  35. tme/__version__.py +1 -1
  36. tme/analyzer/__init__.py +1 -1
  37. tme/analyzer/_utils.py +5 -8
  38. tme/analyzer/aggregation.py +28 -9
  39. tme/analyzer/base.py +25 -36
  40. tme/analyzer/peaks.py +49 -122
  41. tme/analyzer/proxy.py +1 -0
  42. tme/backends/_jax_utils.py +31 -28
  43. tme/backends/_numpyfftw_utils.py +270 -0
  44. tme/backends/cupy_backend.py +11 -54
  45. tme/backends/jax_backend.py +72 -48
  46. tme/backends/matching_backend.py +6 -51
  47. tme/backends/mlx_backend.py +1 -27
  48. tme/backends/npfftw_backend.py +95 -90
  49. tme/backends/pytorch_backend.py +5 -26
  50. tme/density.py +7 -10
  51. tme/extensions.cpython-311-darwin.so +0 -0
  52. tme/filters/__init__.py +2 -2
  53. tme/filters/_utils.py +32 -7
  54. tme/filters/bandpass.py +225 -186
  55. tme/filters/ctf.py +138 -87
  56. tme/filters/reconstruction.py +38 -9
  57. tme/filters/wedge.py +98 -112
  58. tme/filters/whitening.py +1 -6
  59. tme/mask.py +341 -0
  60. tme/matching_data.py +20 -44
  61. tme/matching_exhaustive.py +46 -56
  62. tme/matching_optimization.py +2 -1
  63. tme/matching_scores.py +216 -412
  64. tme/matching_utils.py +82 -424
  65. tme/memory.py +1 -1
  66. tme/orientations.py +16 -8
  67. tme/parser.py +109 -29
  68. tme/preprocessor.py +2 -2
  69. tme/rotations.py +1 -1
  70. pytme-0.3b0.dist-info/RECORD +0 -122
  71. pytme-0.3b0.dist-info/licenses/LICENSE +0 -153
  72. {pytme-0.3b0.dist-info → pytme-0.3.1.dist-info}/WHEEL +0 -0
  73. {pytme-0.3b0.dist-info → pytme-0.3.1.dist-info}/top_level.txt +0 -0
scripts/eval.py ADDED
@@ -0,0 +1,93 @@
1
+ #!python3
2
+ """ Apply tme.preprocessor.Preprocessor methods to an input file based
3
+ on a provided yaml configuration obtaiend from preprocessor_gui.py.
4
+
5
+ Copyright (c) 2023 European Molecular Biology Laboratory
6
+
7
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
8
+ """
9
+ import yaml
10
+ import argparse
11
+ import textwrap
12
+ from tme import Preprocessor, Density
13
+
14
+
15
+ def parse_args():
16
+ parser = argparse.ArgumentParser(
17
+ description=textwrap.dedent(
18
+ """
19
+ Apply preprocessing to an input file based on a provided YAML configuration.
20
+
21
+ Expected YAML file format:
22
+ ```yaml
23
+ <method_name>:
24
+ <parameter1>: <value1>
25
+ <parameter2>: <value2>
26
+ ...
27
+ ```
28
+ """
29
+ ),
30
+ formatter_class=argparse.RawDescriptionHelpFormatter,
31
+ )
32
+ parser.add_argument(
33
+ "-i",
34
+ "--input_file",
35
+ type=str,
36
+ required=True,
37
+ help="Path to the input data file in CCP4/MRC format.",
38
+ )
39
+ parser.add_argument(
40
+ "-y",
41
+ "--yaml_file",
42
+ type=str,
43
+ required=True,
44
+ help="Path to the YAML configuration file.",
45
+ )
46
+ parser.add_argument(
47
+ "-o",
48
+ "--output_file",
49
+ type=str,
50
+ required=True,
51
+ help="Path to output file in CPP4/MRC format..",
52
+ )
53
+ parser.add_argument(
54
+ "--compress", action="store_true", help="Compress the output file using gzip."
55
+ )
56
+
57
+ args = parser.parse_args()
58
+
59
+ return args
60
+
61
+
62
+ def main():
63
+ args = parse_args()
64
+ with open(args.yaml_file, "r") as f:
65
+ preprocess_settings = yaml.safe_load(f)
66
+
67
+ if len(preprocess_settings) > 1:
68
+ raise NotImplementedError(
69
+ "Multiple preprocessing methods specified. "
70
+ "The script currently supports one method at a time."
71
+ )
72
+
73
+ method_name = list(preprocess_settings.keys())[0]
74
+ if not hasattr(Preprocessor, method_name):
75
+ raise ValueError(f"Method {method_name} does not exist in Preprocessor.")
76
+
77
+ density = Density.from_file(args.input_file)
78
+ output = density.empty
79
+
80
+ method_params = preprocess_settings[method_name]
81
+ preprocessor = Preprocessor()
82
+ method = getattr(preprocessor, method_name, None)
83
+ if not method:
84
+ raise ValueError(
85
+ f"{method} does not exist in dge.preprocessor.Preprocessor class."
86
+ )
87
+
88
+ output.data = method(template=density.data, **method_params)
89
+ output.to_file(args.output_file, gzip=args.compress)
90
+
91
+
92
+ if __name__ == "__main__":
93
+ main()
@@ -1,19 +1,22 @@
1
1
  #!python3
2
- """ Prepare orientations stack for refinement.
2
+ """Prepare orientations stack for refinement.
3
3
 
4
- Copyright (c) 2023 European Molecular Biology Laboratory
4
+ Copyright (c) 2023 European Molecular Biology Laboratory
5
5
 
6
- Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
6
+ Author: Valentin Maurer <valentin.maurer@embl-hamburg.de>
7
7
  """
8
8
  import argparse
9
- from os.path import splitext
9
+ from os import unlink
10
+ from os.path import splitext, basename
10
11
 
11
12
  import numpy as np
13
+ from collections import defaultdict
12
14
 
15
+ from tme.parser import StarParser
13
16
  from tme import Density, Orientations
14
- from tme.matching_utils import (
15
- generate_tempfile_name,
16
- rotation_aligning_vectors,
17
+ from tme.matching_utils import generate_tempfile_name
18
+ from tme.rotations import (
19
+ align_vectors,
17
20
  euler_from_rotationmatrix,
18
21
  euler_to_rotationmatrix,
19
22
  )
@@ -25,7 +28,7 @@ class ProgressBar:
25
28
  """
26
29
 
27
30
  def __init__(self, message: str, nchars: int, total: int):
28
- self._size = nchars - len(message) - (len(str(total))+2) * 2
31
+ self._size = nchars - len(message) - (len(str(total)) + 2) * 2
29
32
  self._message = message
30
33
  self._total = total
31
34
 
@@ -45,20 +48,14 @@ def parse_args():
45
48
  )
46
49
 
47
50
  io_group = parser.add_argument_group("Input / Output")
48
- io_group.add_argument(
49
- "--target",
50
- required=True,
51
- type=str,
52
- help="Extract candidates from this target.",
53
- )
54
51
  io_group.add_argument(
55
52
  "--orientations",
56
53
  required=True,
57
54
  type=str,
58
- help="Path to file generated by postprocess.py using output_format orientations.",
55
+ help="Star file with picks and micrograph names.",
59
56
  )
60
57
  io_group.add_argument(
61
- "--orientations_sampling",
58
+ "--orientations-scaling",
62
59
  required=False,
63
60
  type=float,
64
61
  default=1.0,
@@ -67,54 +64,59 @@ def parse_args():
67
64
  )
68
65
  io_group.add_argument(
69
66
  "-o",
70
- "--output_file",
67
+ "--output-prefix",
71
68
  required=True,
72
69
  type=str,
73
- help="Path to write output H5 file.",
70
+ help="Output prefix to use.",
74
71
  )
75
72
 
76
73
  alignment_group = parser.add_argument_group("Alignment")
77
74
  alignment_group.add_argument(
78
- "--align_orientations",
75
+ "--align-orientations",
79
76
  action="store_true",
80
77
  required=False,
81
78
  help="Whether to align extracted orientations based on their angles. Allows "
82
79
  "for efficient subsequent sampling of cone angles.",
83
80
  )
84
81
  alignment_group.add_argument(
85
- "--angles_are_vector",
82
+ "--angles-are-vector",
86
83
  action="store_true",
87
84
  required=False,
88
85
  help="Considers euler_z euler_y, euler_x as vector that will be rotated to align "
89
86
  "with the z-axis (1,0,0). Only considered when --align_orientations is set.",
90
87
  )
91
88
  alignment_group.add_argument(
92
- "--interpolation_order",
93
- dest="interpolation_order",
89
+ "--interpolation-order",
94
90
  required=False,
95
91
  type=int,
96
92
  default=1,
97
93
  help="Interpolation order for alignment, less than zero is no interpolation.",
98
94
  )
95
+ alignment_group.add_argument(
96
+ "--split-by-micrograph",
97
+ action="store_true",
98
+ required=False,
99
+ help="Create separate output files for each micrograph."
100
+ )
99
101
 
100
102
  extraction_group = parser.add_argument_group("Extraction")
101
103
  extraction_group.add_argument(
102
- "--box_size",
103
- required=False,
104
+ "--box-size",
105
+ required=True,
104
106
  type=int,
105
- help="Box size for extraction, defaults to two times the template.",
107
+ help="Box size for extraction.",
106
108
  )
107
109
  extraction_group.add_argument(
108
- "--translation_uncertainty",
110
+ "--translation-uncertainty",
109
111
  required=False,
110
112
  type=int,
111
113
  help="Sets box size for extraction to template box plus this value.",
112
114
  )
113
115
  extraction_group.add_argument(
114
- "--keep_out_of_box",
116
+ "--drop-out-of-box",
115
117
  action="store_true",
116
118
  required=False,
117
- help="Whether to keep orientations that fall outside the box. If the "
119
+ help="Whether to drop orientations that fall outside the box. If the "
118
120
  "orientations are sensible, it is safe to pass this flag.",
119
121
  )
120
122
 
@@ -125,100 +127,117 @@ def parse_args():
125
127
 
126
128
  def main():
127
129
  args = parse_args()
130
+
131
+ data = StarParser(args.orientations, delimiter="\t")
132
+ key = list(data.keys())[0]
133
+
134
+ index_map = defaultdict(list)
135
+ for index, value in enumerate(data[key]["_rlnMicrographName"]):
136
+ index_map[value].append(index)
137
+
128
138
  orientations = Orientations.from_file(args.orientations)
129
139
  orientations.translations = np.divide(
130
- orientations.translations, args.orientations_sampling
140
+ orientations.translations, args.orientations_scaling
131
141
  )
132
142
 
133
- target = Density.from_file(args.target, use_memmap=True)
134
-
135
143
  box_size = np.array(args.box_size)
136
- box_size = np.repeat(box_size, target.data.ndim // box_size.size).astype(int)
137
-
144
+ box_size = np.repeat(box_size, 3 // box_size.size).astype(int)
138
145
  extraction_shape = np.copy(box_size)
139
- if args.align_orientations:
140
- extraction_shape[:] = int(np.linalg.norm(box_size) + 1)
141
-
142
- orientations, cand_slices, obs_slices = orientations.get_extraction_slices(
143
- target_shape=target.shape,
144
- extraction_shape=extraction_shape,
145
- drop_out_of_box=not args.keep_out_of_box,
146
- return_orientations=True,
147
- )
148
146
 
149
147
  if args.align_orientations:
148
+ extraction_shape[:] = int(np.linalg.norm(box_size) + 1)
150
149
  for index in range(orientations.rotations.shape[0]):
151
150
  rotation_matrix = euler_to_rotationmatrix(orientations.rotations[index])
152
151
  rotation_matrix = np.linalg.inv(rotation_matrix)
153
152
  if args.angles_are_vector:
154
- rotation_matrix = rotation_aligning_vectors(
153
+ rotation_matrix = align_vectors(
155
154
  orientations.rotations[index], target_vector=(1, 0, 0)
156
155
  )
157
156
  orientations.rotations[index] = euler_from_rotationmatrix(rotation_matrix)
158
157
 
159
- filename = generate_tempfile_name()
160
- output_dtype = target.data.dtype
161
- if args.align_orientations is not None:
162
- output_dtype = np.float32
163
-
164
- target.data = target.data.astype(output_dtype)
165
-
166
- dens = Density(
167
- np.memmap(
168
- filename,
169
- mode="w+",
170
- shape=(len(obs_slices), *box_size),
171
- dtype=output_dtype,
172
- ),
173
- sampling_rate=(1, *target.sampling_rate),
174
- origin=(0, *target.origin),
175
- )
176
- dens.data[:] = target.metadata["mean"]
177
-
178
- data_subset = np.zeros(extraction_shape, dtype=target.data.dtype)
179
- pbar = ProgressBar(message="Aligning ", nchars=80, total=len(obs_slices))
180
- for index, (obs_slice, cand_slice) in enumerate(zip(obs_slices, cand_slices)):
181
- pbar.update(index + 1)
182
-
183
- data_subset.fill(0)
184
- data_subset[cand_slice] = target.data[obs_slice]
185
- target_subset = Density(
186
- data_subset,
187
- sampling_rate=target.sampling_rate,
188
- origin=target.origin,
158
+ ret_orientations, ret_dens, ix = [], [], 0
159
+ n_particles = orientations.translations.shape[0]
160
+ pbar = ProgressBar(message="Processing ", nchars=80, total=n_particles)
161
+ for target_path, indices in index_map.items():
162
+
163
+ target = Density.from_file(target_path, use_memmap=True)
164
+
165
+ subset = orientations[indices]
166
+ subset, cand_slices, obs_slices = subset.get_extraction_slices(
167
+ target_shape=target.shape,
168
+ extraction_shape=extraction_shape,
169
+ drop_out_of_box=args.drop_out_of_box,
170
+ return_orientations=True,
189
171
  )
190
172
 
191
- if args.align_orientations:
192
- rotation_matrix = euler_to_rotationmatrix(orientations.rotations[index])
193
- target_subset = target_subset.rigid_transform(
194
- rotation_matrix=rotation_matrix,
195
- use_geometric_center=True,
196
- order=args.interpolation_order,
173
+ dens = Density(
174
+ np.memmap(
175
+ generate_tempfile_name(),
176
+ mode="w+",
177
+ shape=(subset.translations.shape[0], *box_size),
178
+ dtype=np.float32,
179
+ ),
180
+ sampling_rate = (1, *target.sampling_rate),
181
+ metadata = {"batch_dimension" : (0,), "path" : target_path}
182
+ )
183
+
184
+ data_subset = np.zeros(extraction_shape, dtype=target.data.dtype)
185
+ for index, (obs_slice, cand_slice) in enumerate(zip(obs_slices, cand_slices)):
186
+ pbar.update(ix + 1)
187
+
188
+ data_subset.fill(0)
189
+ data_subset[cand_slice] = target.data[obs_slice]
190
+ target_subset = Density(
191
+ data_subset,
192
+ sampling_rate=target.sampling_rate,
193
+ origin=target.origin,
197
194
  )
198
- target_subset.pad(box_size, center=True)
199
195
 
200
- # target_value = target.data[tuple(orientations.translations[index].astype(int))]
201
- # center = np.divide(target_subset.data.shape, 2).astype(int)
202
- # print(np.where(target_subset.data == target_value), center)
203
- # print(target_subset.data[tuple(center.astype(int))],
204
- # target_value,
205
- # target_subset.data[tuple(center.astype(int))] == target_value
206
- # )
196
+ if args.align_orientations:
197
+ rotation_matrix = euler_to_rotationmatrix(subset.rotations[index])
198
+ target_subset = target_subset.rigid_transform(
199
+ rotation_matrix=rotation_matrix,
200
+ use_geometric_center=True,
201
+ order=args.interpolation_order,
202
+ )
203
+ target_subset.pad(box_size, center=True)
204
+ dens.data[index] = target_subset.data.astype(np.float32)
205
+ ix += 1
207
206
 
208
- dens.data[index] = target_subset.data
209
- print("")
207
+ ret_dens.append(dens)
208
+ ret_orientations.append(subset)
210
209
 
211
- target_meta = {
212
- k: v for k, v in target.metadata.items() if k in ("mean", "max", "min", "std")
213
- }
214
- dens.metadata.update(target_meta)
215
- dens.metadata["batch_dimension"] = (0,)
216
- dens.metadata["normals"] = orientations.rotations
210
+ if not len(ret_dens):
211
+ exit("Found no valid particles.")
217
212
 
218
- dens.to_file(args.output_file)
219
- orientations.to_file(
220
- f"{splitext(args.output_file)[0]}_aligned.tsv", file_format="text"
221
- )
213
+ print("")
214
+ if not args.split_by_micrograph:
215
+ ret_orientations = [Orientations(
216
+ translations=np.concatenate([x.translations for x in ret_orientations]),
217
+ rotations=np.concatenate([x.rotations for x in ret_orientations]),
218
+ scores=np.concatenate([x.scores for x in ret_orientations]),
219
+ details=np.concatenate([x.details for x in ret_orientations]),
220
+ )]
221
+ dens_data = Density(
222
+ np.concatenate([x.data for x in ret_dens]),
223
+ sampling_rate=ret_dens[0].sampling_rate
224
+ )
225
+ _ = [unlink(x.data.filename) for x in ret_dens]
226
+ dens_data.metadata.update({"batch_dimension" : (0, )})
227
+ ret_dens = [dens_data]
228
+
229
+ for orientation, dens in zip(ret_orientations, ret_dens):
230
+ fname = args.output_prefix
231
+ if args.split_by_micrograph:
232
+ target = splitext(basename(dens.metadata["path"]))[0]
233
+ fname = f"{args.output_prefix}_{target}"
234
+
235
+ dens.to_file(f"{fname}.h5")
236
+ orientation.to_file(f"{fname}_aligned.star")
237
+ try:
238
+ unlink(dens.data.filename)
239
+ except Exception:
240
+ continue
222
241
 
223
242
  if __name__ == "__main__":
224
243
  main()