pytme 0.2.3__cp311-cp311-macosx_14_0_arm64.whl → 0.2.4__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.
Files changed (75) hide show
  1. {pytme-0.2.3.data → pytme-0.2.4.data}/scripts/match_template.py +8 -8
  2. {pytme-0.2.3.data → pytme-0.2.4.data}/scripts/preprocess.py +22 -6
  3. {pytme-0.2.3.data → pytme-0.2.4.data}/scripts/preprocessor_gui.py +9 -14
  4. {pytme-0.2.3.dist-info → pytme-0.2.4.dist-info}/METADATA +1 -1
  5. pytme-0.2.4.dist-info/RECORD +119 -0
  6. {pytme-0.2.3.dist-info → pytme-0.2.4.dist-info}/WHEEL +1 -1
  7. {pytme-0.2.3.dist-info → pytme-0.2.4.dist-info}/top_level.txt +1 -0
  8. scripts/match_template.py +8 -8
  9. scripts/preprocess.py +22 -6
  10. scripts/preprocessor_gui.py +9 -14
  11. tests/__init__.py +0 -0
  12. tests/data/.DS_Store +0 -0
  13. tests/data/Blurring/.DS_Store +0 -0
  14. tests/data/Blurring/blob_width18.npy +0 -0
  15. tests/data/Blurring/edgegaussian_sigma3.npy +0 -0
  16. tests/data/Blurring/gaussian_sigma2.npy +0 -0
  17. tests/data/Blurring/hamming_width6.npy +0 -0
  18. tests/data/Blurring/kaiserb_width18.npy +0 -0
  19. tests/data/Blurring/localgaussian_sigma0510.npy +0 -0
  20. tests/data/Blurring/mean_size5.npy +0 -0
  21. tests/data/Blurring/ntree_sigma0510.npy +0 -0
  22. tests/data/Blurring/rank_rank3.npy +0 -0
  23. tests/data/Maps/.DS_Store +0 -0
  24. tests/data/Maps/emd_8621.mrc.gz +0 -0
  25. tests/data/README.md +2 -0
  26. tests/data/Raw/.DS_Store +0 -0
  27. tests/data/Raw/em_map.map +0 -0
  28. tests/data/Structures/.DS_Store +0 -0
  29. tests/data/Structures/1pdj.cif +3339 -0
  30. tests/data/Structures/1pdj.pdb +1429 -0
  31. tests/data/Structures/5khe.cif +3685 -0
  32. tests/data/Structures/5khe.ent +2210 -0
  33. tests/data/Structures/5khe.pdb +2210 -0
  34. tests/data/Structures/5uz4.cif +70548 -0
  35. tests/preprocessing/__init__.py +0 -0
  36. tests/preprocessing/test_compose.py +76 -0
  37. tests/preprocessing/test_frequency_filters.py +178 -0
  38. tests/preprocessing/test_preprocessor.py +136 -0
  39. tests/preprocessing/test_utils.py +79 -0
  40. tests/test_analyzer.py +310 -0
  41. tests/test_backends.py +375 -0
  42. tests/test_density.py +508 -0
  43. tests/test_extensions.py +130 -0
  44. tests/test_matching_cli.py +283 -0
  45. tests/test_matching_data.py +162 -0
  46. tests/test_matching_exhaustive.py +162 -0
  47. tests/test_matching_memory.py +30 -0
  48. tests/test_matching_optimization.py +276 -0
  49. tests/test_matching_utils.py +326 -0
  50. tests/test_orientations.py +173 -0
  51. tests/test_packaging.py +95 -0
  52. tests/test_parser.py +33 -0
  53. tests/test_structure.py +243 -0
  54. tme/__init__.py +0 -1
  55. tme/__version__.py +1 -1
  56. tme/backends/jax_backend.py +8 -7
  57. tme/data/scattering_factors.pickle +0 -0
  58. tme/density.py +11 -4
  59. tme/external/bindings.cpp +332 -0
  60. tme/matching_data.py +11 -9
  61. tme/matching_exhaustive.py +10 -8
  62. tme/matching_utils.py +1 -0
  63. tme/preprocessing/_utils.py +14 -14
  64. tme/preprocessing/composable_filter.py +0 -2
  65. tme/preprocessing/compose.py +4 -4
  66. tme/preprocessing/frequency_filters.py +32 -35
  67. tme/preprocessing/tilt_series.py +202 -118
  68. tme/preprocessor.py +24 -246
  69. tme/structure.py +14 -14
  70. pytme-0.2.3.dist-info/RECORD +0 -75
  71. tme/matching_memory.py +0 -383
  72. {pytme-0.2.3.data → pytme-0.2.4.data}/scripts/estimate_ram_usage.py +0 -0
  73. {pytme-0.2.3.data → pytme-0.2.4.data}/scripts/postprocess.py +0 -0
  74. {pytme-0.2.3.dist-info → pytme-0.2.4.dist-info}/LICENSE +0 -0
  75. {pytme-0.2.3.dist-info → pytme-0.2.4.dist-info}/entry_points.txt +0 -0
@@ -10,11 +10,11 @@ from dataclasses import dataclass
10
10
 
11
11
  import numpy as np
12
12
 
13
- from .. import Preprocessor
14
13
  from ..types import NDArray
15
14
  from ..backends import backend as be
16
- from ..matching_utils import euler_to_rotationmatrix
15
+ from ..matching_utils import euler_to_rotationmatrix, centered
17
16
  from ._utils import (
17
+ centered_grid,
18
18
  frequency_grid_at_angle,
19
19
  compute_tilt_shape,
20
20
  crop_real_fourier,
@@ -90,14 +90,10 @@ def create_reconstruction_filter(
90
90
  tilt_angles = kwargs.get("tilt_angles", False)
91
91
  if tilt_angles is False:
92
92
  raise ValueError("'ramp' filter requires specifying tilt angles.")
93
- size, odd = filter_shape[0], filter_shape[0] % 2
94
- ret = np.arange(-size // 2 + odd, size // 2 + odd, 1, dtype=np.float32)
95
- ret /= size // 2
96
- ret *= 0.5
97
- np.abs(ret, out=ret)
98
-
93
+ size = filter_shape[0]
94
+ ret = fftfreqn((size,), sampling_rate = 1, compute_euclidean_norm = True)
99
95
  min_increment = np.radians(np.min(np.abs(np.diff(np.sort(tilt_angles)))))
100
- ret *= min_increment * size
96
+ ret *= (min_increment * size)
101
97
  np.fmin(ret, 1, out=ret)
102
98
 
103
99
  ret = np.tile(ret[:, np.newaxis], (1, filter_shape[1]))
@@ -164,8 +160,8 @@ class ReconstructFromTilt:
164
160
  """
165
161
  Reconstruct a volume from a tilt series.
166
162
 
167
- Parameters:
168
- -----------
163
+ Parameters
164
+ ----------
169
165
  data : NDArray
170
166
  The tilt series data.
171
167
  shape : tuple of int
@@ -177,15 +173,15 @@ class ReconstructFromTilt:
177
173
  tilt_axis : int
178
174
  Axis the plane is tilted over.
179
175
  interpolation_order : int, optional
180
- Interpolation order used for rotation, defautls to 1.
176
+ Interpolation order used for rotation, defaults to 1.
181
177
  return_real_fourier : bool, optional
182
178
  Whether to return a shape compliant with rfftn, defaults to True.
183
179
  reconstruction_filter : bool, optional
184
180
  Filter window applied during reconstruction.
185
181
  See :py:meth:`create_reconstruction_filter` for available options.
186
182
 
187
- Returns:
188
- --------
183
+ Returns
184
+ -------
189
185
  NDArray
190
186
  The reconstructed volume.
191
187
  """
@@ -197,7 +193,7 @@ class ReconstructFromTilt:
197
193
  volume_temp_rotated = be.zeros(shape, dtype=be._float_dtype)
198
194
  volume = be.zeros(shape, dtype=be._float_dtype)
199
195
 
200
- slices = tuple(slice(a, a + 1) for a in be.astype(be.divide(shape, 2), int))
196
+ slices = tuple(slice(a//2, (a//2) + 1) for a in shape)
201
197
  subset = tuple(
202
198
  slice(None) if i != opening_axis else slices[opening_axis]
203
199
  for i in range(len(shape))
@@ -224,6 +220,7 @@ class ReconstructFromTilt:
224
220
  rec_filter = be.to_backend_array(rec_filter)
225
221
  rec_filter = be.reshape(rec_filter, wedges[0].shape)
226
222
 
223
+ angles = be.to_backend_array(angles)
227
224
  for index in range(len(angles)):
228
225
  be.fill(angles_loop, 0)
229
226
  be.fill(volume_temp, 0)
@@ -257,8 +254,8 @@ class Wedge:
257
254
  """
258
255
  Generate wedge mask for tomographic data.
259
256
 
260
- Parameters:
261
- -----------
257
+ Parameters
258
+ ----------
262
259
  shape : tuple of int
263
260
  The shape of the reconstruction volume.
264
261
  tilt_axis : int
@@ -272,10 +269,10 @@ class Wedge:
272
269
  weight_type : str, optional
273
270
  The type of weighting to apply, defaults to None.
274
271
  frequency_cutoff : float, optional
275
- The frequency cutoff value, defaults to 0.5.
272
+ Frequency cutoff for created mask. Nyquist 0.5 by default.
276
273
 
277
- Returns:
278
- --------
274
+ Returns
275
+ -------
279
276
  Dict
280
277
  A dictionary containing weighted wedges and related information.
281
278
  """
@@ -303,13 +300,13 @@ class Wedge:
303
300
  Generate a :py:class:`Wedge` instance by reading tilt angles and weights
304
301
  from a tab-separated text file.
305
302
 
306
- Parameters:
307
- -----------
303
+ Parameters
304
+ ----------
308
305
  filename : str
309
306
  The path to the file containing tilt angles and weights.
310
307
 
311
- Returns:
312
- --------
308
+ Returns
309
+ -------
313
310
  :py:class:`Wedge`
314
311
  Class instance instance initialized with angles and weights from the file.
315
312
  """
@@ -338,15 +335,15 @@ class Wedge:
338
335
  """
339
336
  Read column data from a text file.
340
337
 
341
- Parameters:
342
- -----------
338
+ Parameters
339
+ ----------
343
340
  filename : str
344
341
  The path to the text file.
345
342
  delimiter : str, optional
346
343
  The delimiter used in the file, defaults to '\t'.
347
344
 
348
- Returns:
349
- --------
345
+ Returns
346
+ -------
350
347
  Dict
351
348
  A dictionary with one key for each column.
352
349
  """
@@ -380,6 +377,19 @@ class Wedge:
380
377
  func_args["weights"] = np.cos(np.radians(self.angles))
381
378
 
382
379
  ret = weight_types[weight_type](**func_args)
380
+
381
+ frequency_cutoff = func_args.get("frequency_cutoff", None)
382
+ if frequency_cutoff is not None:
383
+ for index, angle in enumerate(self.angles):
384
+ frequency_grid = frequency_grid_at_angle(
385
+ shape=func_args["shape"],
386
+ opening_axis=self.opening_axis,
387
+ tilt_axis=self.tilt_axis,
388
+ angle=angle,
389
+ sampling_rate=1,
390
+ )
391
+ ret[index] = np.multiply(ret[index], frequency_grid <= frequency_cutoff)
392
+
383
393
  ret = be.astype(be.to_backend_array(ret), be._float_dtype)
384
394
 
385
395
  return {
@@ -413,46 +423,50 @@ class Wedge:
413
423
 
414
424
  return wedges
415
425
 
416
- def weight_relion(self, **kwargs) -> NDArray:
426
+ def weight_relion(self,
427
+ shape: Tuple[int],
428
+ opening_axis: int,
429
+ tilt_axis: int,
430
+ **kwargs
431
+ ) -> NDArray:
417
432
  """
418
433
  Generate weighted wedges based on the RELION 1.4 formalism, weighting each
419
434
  angle using the cosine of the angle and a Gaussian lowpass filter computed
420
435
  with respect to the exposure per angstrom.
421
436
 
422
- Returns:
423
- --------
437
+ Returns
438
+ -------
424
439
  NDArray
425
440
  Weighted wedges.
426
441
  """
427
442
  tilt_shape = compute_tilt_shape(
428
- shape=self.shape, opening_axis=self.opening_axis, reduce_dim=True
443
+ shape=shape, opening_axis=opening_axis, reduce_dim=True
429
444
  )
430
445
 
431
446
  wedges = np.zeros((len(self.angles), *tilt_shape))
432
447
  for index, angle in enumerate(self.angles):
433
448
  frequency_grid = frequency_grid_at_angle(
434
- shape=self.shape,
435
- opening_axis=self.opening_axis,
436
- tilt_axis=self.tilt_axis,
449
+ shape=shape,
450
+ opening_axis=opening_axis,
451
+ tilt_axis=tilt_axis,
437
452
  angle=angle,
438
453
  sampling_rate=1,
439
454
  )
440
- # frequency_mask = frequency_grid <= self.frequency_cutoff
441
-
442
455
  sigma = np.sqrt(self.weights[index] * 4 / (8 * np.pi**2))
443
456
  sigma = -2 * np.pi**2 * sigma**2
444
457
  np.square(frequency_grid, out=frequency_grid)
445
458
  np.multiply(sigma, frequency_grid, out=frequency_grid)
446
459
  np.exp(frequency_grid, out=frequency_grid)
447
460
  np.multiply(frequency_grid, np.cos(np.radians(angle)), out=frequency_grid)
448
- # np.multiply(frequency_grid, frequency_mask, out=frequency_grid)
449
-
450
461
  wedges[index] = frequency_grid
451
462
 
452
463
  return wedges
453
464
 
454
465
  def weight_grigorieff(
455
466
  self,
467
+ shape: Tuple[int],
468
+ opening_axis: int,
469
+ tilt_axis: int,
456
470
  amplitude: float = 0.245,
457
471
  power: float = -1.665,
458
472
  offset: float = 2.81,
@@ -461,31 +475,28 @@ class Wedge:
461
475
  """
462
476
  Generate weighted wedges based on the formalism introduced in [1]_.
463
477
 
464
- Returns:
465
- --------
478
+ Returns
479
+ -------
466
480
  NDArray
467
481
  Weighted wedges.
468
482
 
469
483
  References
470
484
  ----------
471
- .. [1] Timothy GrantNikolaus Grigorieff (2015), eLife 4:e06980.
485
+ .. [1] Timothy Grant, Nikolaus Grigorieff (2015), eLife 4:e06980.
472
486
  """
473
487
  tilt_shape = compute_tilt_shape(
474
- shape=self.shape,
475
- opening_axis=self.opening_axis,
476
- reduce_dim=True,
488
+ shape=shape, opening_axis=opening_axis, reduce_dim=True
477
489
  )
478
490
 
479
491
  wedges = np.zeros((len(self.angles), *tilt_shape), dtype=be._float_dtype)
480
492
  for index, angle in enumerate(self.angles):
481
493
  frequency_grid = frequency_grid_at_angle(
482
- shape=self.shape,
483
- opening_axis=self.opening_axis,
484
- tilt_axis=self.tilt_axis,
494
+ shape=shape,
495
+ opening_axis=opening_axis,
496
+ tilt_axis=tilt_axis,
485
497
  angle=angle,
486
498
  sampling_rate=1,
487
499
  )
488
- # frequency_mask = frequency_grid <= self.frequency_cutoff
489
500
 
490
501
  with np.errstate(divide="ignore"):
491
502
  np.power(frequency_grid, power, out=frequency_grid)
@@ -498,10 +509,7 @@ class Wedge:
498
509
  out=frequency_grid,
499
510
  )
500
511
 
501
- np.exp(frequency_grid, out=frequency_grid)
502
- # np.multiply(frequency_grid, frequency_mask, out=frequency_grid)
503
-
504
- wedges[index] = frequency_grid
512
+ wedges[index] = np.exp(frequency_grid)
505
513
 
506
514
  return wedges
507
515
 
@@ -510,14 +518,24 @@ class WedgeReconstructed:
510
518
  """
511
519
  Initialize :py:class:`WedgeReconstructed`.
512
520
 
513
- Parameters:
514
- -----------
515
- angles : Tuple[float], optional
521
+ Parameters
522
+ ----------
523
+ angles :tuple of float, optional
516
524
  The tilt angles, defaults to None.
517
525
  opening_axis : int, optional
518
526
  The axis around which the wedge is opened, defaults to 0.
519
527
  tilt_axis : int, optional
520
528
  The axis along which the tilt is applied, defaults to 2.
529
+ weights : tuple of float, optional
530
+ Weights to assign to individual wedge components.
531
+ weight_wedge : bool, optional
532
+ Whether individual wedge components should be weighted. If True and weights
533
+ is None, uses the cosine of the angle otherwise weights.
534
+ create_continuous_wedge: bool, optional
535
+ Whether to create a continous wedge or a per-component wedge. Weights are only
536
+ considered for non-continuous wedges.
537
+ frequency_cutoff : float, optional
538
+ Filter window applied during reconstruction.
521
539
  **kwargs : Dict
522
540
  Additional keyword arguments.
523
541
  """
@@ -525,33 +543,37 @@ class WedgeReconstructed:
525
543
  def __init__(
526
544
  self,
527
545
  angles: Tuple[float] = None,
528
- start_tilt: float = None,
529
- stop_tilt: float = None,
530
546
  opening_axis: int = 0,
531
547
  tilt_axis: int = 2,
548
+ weights : Tuple[float] = None,
532
549
  weight_wedge: bool = False,
533
550
  create_continuous_wedge: bool = False,
551
+ frequency_cutoff: float = 0.5,
552
+ reconstruction_filter: str = None,
534
553
  **kwargs: Dict,
535
554
  ):
536
555
  self.angles = angles
537
556
  self.opening_axis = opening_axis
538
557
  self.tilt_axis = tilt_axis
558
+ self.weights = weights
539
559
  self.weight_wedge = weight_wedge
560
+ self.reconstruction_filter = reconstruction_filter
540
561
  self.create_continuous_wedge = create_continuous_wedge
562
+ self.frequency_cutoff = frequency_cutoff
541
563
 
542
564
  def __call__(self, shape: Tuple[int], **kwargs: Dict) -> Dict:
543
565
  """
544
566
  Generate the reconstructed wedge.
545
567
 
546
- Parameters:
547
- -----------
568
+ Parameters
569
+ ----------
548
570
  shape : tuple of int
549
571
  The shape of the reconstruction volume.
550
572
  **kwargs : Dict
551
573
  Additional keyword arguments.
552
574
 
553
- Returns:
554
- --------
575
+ Returns
576
+ -------
555
577
  Dict
556
578
  A dictionary containing the reconstructed wedge and related information.
557
579
  """
@@ -559,15 +581,39 @@ class WedgeReconstructed:
559
581
  func_args.update(kwargs)
560
582
 
561
583
  if kwargs.get("is_fourier_shape", False):
562
- print("Cannot create continuous wedge mask basde on real fourier shape.")
584
+ print("Cannot create continuous wedge mask based on real fourier shape.")
563
585
 
564
586
  func = self.step_wedge
565
587
  if func_args.get("create_continuous_wedge", False):
566
588
  func = self.continuous_wedge
567
589
 
590
+ weight_wedge = func_args.get("weight_wedge", False)
591
+ if func_args.get("wedge_weights") is None and weight_wedge:
592
+ func_args["weights"] = np.cos(
593
+ np.radians(be.to_numpy_array(func_args.get("angles", (0,))))
594
+ )
595
+
568
596
  ret = func(shape=shape, **func_args)
597
+
598
+ frequency_cutoff = func_args.get("frequency_cutoff", None)
599
+ if frequency_cutoff is not None:
600
+ frequency_mask = fftfreqn(
601
+ shape=shape,
602
+ sampling_rate=1,
603
+ compute_euclidean_norm=True,
604
+ shape_is_real_fourier=False,
605
+ )
606
+ ret = np.multiply(ret, frequency_mask <= frequency_cutoff, out=ret)
607
+
608
+ if not weight_wedge:
609
+ ret = (ret > 0) * 1.0
610
+
569
611
  ret = be.astype(be.to_backend_array(ret), be._float_dtype)
570
612
 
613
+ ret = shift_fourier(data=ret, shape_is_real_fourier=False)
614
+ if func_args.get("return_real_fourier", False):
615
+ ret = crop_real_fourier(ret)
616
+
571
617
  return {
572
618
  "data": ret,
573
619
  "shape_is_real_fourier": func_args["return_real_fourier"],
@@ -584,14 +630,13 @@ class WedgeReconstructed:
584
630
  angles: Tuple[float],
585
631
  opening_axis: int,
586
632
  tilt_axis: int,
587
- return_real_fourier: bool,
588
633
  **kwargs: Dict,
589
634
  ) -> NDArray:
590
635
  """
591
- Generate a continuous reconstructed wedge.
636
+ Generate a continous wedge mask with DC component at the center.
592
637
 
593
- Parameters:
594
- -----------
638
+ Parameters
639
+ ----------
595
640
  shape : tuple of int
596
641
  The shape of the reconstruction volume.
597
642
  angles : tuple of float
@@ -600,27 +645,28 @@ class WedgeReconstructed:
600
645
  The axis around which the wedge is opened.
601
646
  tilt_axis : int
602
647
  The axis along which the tilt is applied.
603
- return_real_fourier : bool
604
- Whether to return the real part of the Fourier transform.
605
648
 
606
- Returns:
607
- --------
649
+ Returns
650
+ -------
608
651
  NDArray
609
- The reconstructed wedge.
652
+ Wedge mask.
610
653
  """
611
- preprocessor = Preprocessor()
612
- start_tilt, stop_tilt = angles
613
- ret = preprocessor.continuous_wedge_mask(
614
- start_tilt=start_tilt,
615
- stop_tilt=stop_tilt,
616
- shape=shape,
617
- opening_axis=opening_axis,
618
- tilt_axis=tilt_axis,
619
- omit_negative_frequencies=return_real_fourier,
620
- infinite_plane=False,
654
+ start_radians = np.tan(np.radians(90 - angles[0]))
655
+ stop_radians = np.tan(np.radians(-1 * (90 - angles[1])))
656
+
657
+ grid = centered_grid(shape)
658
+ with np.errstate(divide="ignore", invalid="ignore"):
659
+ ratios = np.where(
660
+ grid[opening_axis] == 0,
661
+ np.tan(np.radians(90)) + 1,
662
+ grid[tilt_axis] / grid[opening_axis],
663
+ )
664
+
665
+ wedge = np.logical_or(start_radians <= ratios, stop_radians >= ratios).astype(
666
+ np.float32
621
667
  )
622
668
 
623
- return ret
669
+ return wedge
624
670
 
625
671
  @staticmethod
626
672
  def step_wedge(
@@ -628,15 +674,15 @@ class WedgeReconstructed:
628
674
  angles: Tuple[float],
629
675
  opening_axis: int,
630
676
  tilt_axis: int,
631
- return_real_fourier: bool,
632
- weight_wedge: bool = False,
677
+ weights: Tuple[float] = None,
678
+ reconstruction_filter : str = None,
633
679
  **kwargs: Dict,
634
680
  ) -> NDArray:
635
681
  """
636
- Generate a step-wise reconstructed wedge.
682
+ Generate a per-angle wedge shape with DC component at the center.
637
683
 
638
- Parameters:
639
- -----------
684
+ Parameters
685
+ ----------
640
686
  shape : tuple of int
641
687
  The shape of the reconstruction volume.
642
688
  angles : tuple of float
@@ -645,38 +691,76 @@ class WedgeReconstructed:
645
691
  The axis around which the wedge is opened.
646
692
  tilt_axis : int
647
693
  The axis along which the tilt is applied.
648
- weight_wedge : bool, optional
649
- Whether to weight the wedge by the cosine of the angle.
650
- return_real_fourier : bool
651
- Whether to return the real part of the Fourier transform.
694
+ reconstruction_filter : str
695
+ Filter used during reconstruction.
696
+ weights : tuple of float, optional
697
+ Weights to assign to individual tilts. Defaults to 1.
652
698
 
653
- Returns:
654
- --------
699
+ Returns
700
+ -------
655
701
  NDArray
656
- The reconstructed wedge.
702
+ Wege mask.
657
703
  """
658
- preprocessor = Preprocessor()
704
+ from ..backends import NumpyFFTWBackend
659
705
 
660
706
  angles = np.asarray(be.to_numpy_array(angles))
661
- weights = np.ones(angles.size)
662
- if weight_wedge:
663
- weights = np.cos(np.radians(angles))
664
- ret = preprocessor.step_wedge_mask(
665
- tilt_angles=angles,
666
- weights=weights,
667
- start_tilt=None,
668
- stop_tilt=None,
669
- tilt_step=None,
670
- shape=shape,
671
- opening_axis=opening_axis,
672
- tilt_axis=tilt_axis,
673
- omit_negative_frequencies=return_real_fourier,
707
+
708
+ if weights is None:
709
+ weights = np.ones(angles.size)
710
+ weights = np.asarray(weights)
711
+
712
+ shape = tuple(int(x) for x in shape)
713
+ opening_axis, tilt_axis = int(opening_axis), int(tilt_axis)
714
+
715
+ weights = np.repeat(weights, angles.size // weights.size)
716
+ plane = np.zeros(
717
+ (shape[opening_axis], shape[tilt_axis] + (1 - shape[tilt_axis] % 2)),
718
+ dtype=np.float32
674
719
  )
675
720
 
676
- if not weight_wedge:
677
- ret = (ret > 0) * 1.0
721
+ # plane = np.zeros((shape[opening_axis], int(2 * np.max(shape)) + 1), dtype=np.float32)
678
722
 
679
- return ret
723
+ rec_filter = 1
724
+ if reconstruction_filter is not None:
725
+ rec_filter = create_reconstruction_filter(
726
+ plane.shape[::-1], filter_type = reconstruction_filter, tilt_angles = angles
727
+ ).T
728
+
729
+ subset = tuple(
730
+ slice(None) if i != 0 else slice(x // 2, x // 2 + 1)
731
+ for i, x in enumerate(plane.shape)
732
+ )
733
+ plane_rotated, wedge_volume = np.zeros_like(plane), np.zeros_like(plane)
734
+ for index in range(angles.shape[0]):
735
+ plane_rotated.fill(0)
736
+ plane[subset] = 1
737
+ rotation_matrix = euler_to_rotationmatrix((angles[index], 0))
738
+ rotation_matrix = rotation_matrix[np.ix_((0, 1), (0, 1))]
739
+
740
+ NumpyFFTWBackend().rigid_transform(
741
+ arr=plane * rec_filter,
742
+ rotation_matrix=rotation_matrix,
743
+ out=plane_rotated,
744
+ use_geometric_center=True,
745
+ order=1,
746
+ )
747
+ wedge_volume += plane_rotated * weights[index]
748
+
749
+ wedge_volume = centered(wedge_volume, (shape[opening_axis], shape[tilt_axis]))
750
+ np.fmin(wedge_volume, np.max(weights), wedge_volume)
751
+
752
+ if opening_axis > tilt_axis:
753
+ wedge_volume = np.moveaxis(wedge_volume, 1, 0)
754
+
755
+ reshape_dimensions = tuple(
756
+ x if i in (opening_axis, tilt_axis) else 1 for i, x in enumerate(shape)
757
+ )
758
+
759
+ wedge_volume = wedge_volume.reshape(reshape_dimensions)
760
+ tile_dimensions = np.divide(shape, reshape_dimensions).astype(int)
761
+ wedge_volume = np.tile(wedge_volume, tile_dimensions)
762
+
763
+ return wedge_volume
680
764
 
681
765
 
682
766
  @dataclass
@@ -726,8 +810,8 @@ class CTF:
726
810
  """
727
811
  Initialize :py:class:`CTF` from file.
728
812
 
729
- Parameters:
730
- -----------
813
+ Parameters
814
+ ----------
731
815
  filename : str
732
816
  The path to a file with ctf parameters. Supports the following formats:
733
817
  - CTFFIND4
@@ -857,8 +941,8 @@ class CTF:
857
941
  """
858
942
  Compute the CTF weight tilt stack.
859
943
 
860
- Parameters:
861
- -----------
944
+ Parameters
945
+ ----------
862
946
  shape : tuple of int
863
947
  The shape of the CTF.
864
948
  defocus_x : tuple of float
@@ -890,8 +974,8 @@ class CTF:
890
974
  **kwargs : Dict
891
975
  Additional keyword arguments.
892
976
 
893
- Returns:
894
- --------
977
+ Returns
978
+ -------
895
979
  NDArray
896
980
  A stack containing the CTF weight.
897
981
  """