pytme 0.2.3__cp311-cp311-macosx_14_0_arm64.whl → 0.2.5__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 (76) hide show
  1. {pytme-0.2.3.data → pytme-0.2.5.data}/scripts/match_template.py +8 -8
  2. {pytme-0.2.3.data → pytme-0.2.5.data}/scripts/preprocess.py +22 -6
  3. {pytme-0.2.3.data → pytme-0.2.5.data}/scripts/preprocessor_gui.py +9 -14
  4. {pytme-0.2.3.dist-info → pytme-0.2.5.dist-info}/METADATA +1 -1
  5. pytme-0.2.5.dist-info/RECORD +119 -0
  6. {pytme-0.2.3.dist-info → pytme-0.2.5.dist-info}/WHEEL +1 -1
  7. {pytme-0.2.3.dist-info → pytme-0.2.5.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 +226 -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 +3 -9
  57. tme/data/scattering_factors.pickle +0 -0
  58. tme/density.py +14 -10
  59. tme/external/bindings.cpp +332 -0
  60. tme/matching_data.py +14 -12
  61. tme/matching_exhaustive.py +17 -15
  62. tme/matching_optimization.py +215 -208
  63. tme/matching_utils.py +1 -0
  64. tme/preprocessing/_utils.py +14 -14
  65. tme/preprocessing/composable_filter.py +0 -2
  66. tme/preprocessing/compose.py +4 -4
  67. tme/preprocessing/frequency_filters.py +32 -35
  68. tme/preprocessing/tilt_series.py +198 -117
  69. tme/preprocessor.py +24 -246
  70. tme/structure.py +22 -22
  71. pytme-0.2.3.dist-info/RECORD +0 -75
  72. tme/matching_memory.py +0 -383
  73. {pytme-0.2.3.data → pytme-0.2.5.data}/scripts/estimate_ram_usage.py +0 -0
  74. {pytme-0.2.3.data → pytme-0.2.5.data}/scripts/postprocess.py +0 -0
  75. {pytme-0.2.3.dist-info → pytme-0.2.5.dist-info}/LICENSE +0 -0
  76. {pytme-0.2.3.dist-info → pytme-0.2.5.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,12 +90,8 @@ 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
96
  ret *= min_increment * size
101
97
  np.fmin(ret, 1, out=ret)
@@ -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,47 @@ class Wedge:
413
423
 
414
424
  return wedges
415
425
 
416
- def weight_relion(self, **kwargs) -> NDArray:
426
+ def weight_relion(
427
+ self, shape: Tuple[int], opening_axis: int, tilt_axis: int, **kwargs
428
+ ) -> NDArray:
417
429
  """
418
430
  Generate weighted wedges based on the RELION 1.4 formalism, weighting each
419
431
  angle using the cosine of the angle and a Gaussian lowpass filter computed
420
432
  with respect to the exposure per angstrom.
421
433
 
422
- Returns:
423
- --------
434
+ Returns
435
+ -------
424
436
  NDArray
425
437
  Weighted wedges.
426
438
  """
427
439
  tilt_shape = compute_tilt_shape(
428
- shape=self.shape, opening_axis=self.opening_axis, reduce_dim=True
440
+ shape=shape, opening_axis=opening_axis, reduce_dim=True
429
441
  )
430
442
 
431
443
  wedges = np.zeros((len(self.angles), *tilt_shape))
432
444
  for index, angle in enumerate(self.angles):
433
445
  frequency_grid = frequency_grid_at_angle(
434
- shape=self.shape,
435
- opening_axis=self.opening_axis,
436
- tilt_axis=self.tilt_axis,
446
+ shape=shape,
447
+ opening_axis=opening_axis,
448
+ tilt_axis=tilt_axis,
437
449
  angle=angle,
438
450
  sampling_rate=1,
439
451
  )
440
- # frequency_mask = frequency_grid <= self.frequency_cutoff
441
-
442
452
  sigma = np.sqrt(self.weights[index] * 4 / (8 * np.pi**2))
443
453
  sigma = -2 * np.pi**2 * sigma**2
444
454
  np.square(frequency_grid, out=frequency_grid)
445
455
  np.multiply(sigma, frequency_grid, out=frequency_grid)
446
456
  np.exp(frequency_grid, out=frequency_grid)
447
457
  np.multiply(frequency_grid, np.cos(np.radians(angle)), out=frequency_grid)
448
- # np.multiply(frequency_grid, frequency_mask, out=frequency_grid)
449
-
450
458
  wedges[index] = frequency_grid
451
459
 
452
460
  return wedges
453
461
 
454
462
  def weight_grigorieff(
455
463
  self,
464
+ shape: Tuple[int],
465
+ opening_axis: int,
466
+ tilt_axis: int,
456
467
  amplitude: float = 0.245,
457
468
  power: float = -1.665,
458
469
  offset: float = 2.81,
@@ -461,31 +472,28 @@ class Wedge:
461
472
  """
462
473
  Generate weighted wedges based on the formalism introduced in [1]_.
463
474
 
464
- Returns:
465
- --------
475
+ Returns
476
+ -------
466
477
  NDArray
467
478
  Weighted wedges.
468
479
 
469
480
  References
470
481
  ----------
471
- .. [1] Timothy GrantNikolaus Grigorieff (2015), eLife 4:e06980.
482
+ .. [1] Timothy Grant, Nikolaus Grigorieff (2015), eLife 4:e06980.
472
483
  """
473
484
  tilt_shape = compute_tilt_shape(
474
- shape=self.shape,
475
- opening_axis=self.opening_axis,
476
- reduce_dim=True,
485
+ shape=shape, opening_axis=opening_axis, reduce_dim=True
477
486
  )
478
487
 
479
488
  wedges = np.zeros((len(self.angles), *tilt_shape), dtype=be._float_dtype)
480
489
  for index, angle in enumerate(self.angles):
481
490
  frequency_grid = frequency_grid_at_angle(
482
- shape=self.shape,
483
- opening_axis=self.opening_axis,
484
- tilt_axis=self.tilt_axis,
491
+ shape=shape,
492
+ opening_axis=opening_axis,
493
+ tilt_axis=tilt_axis,
485
494
  angle=angle,
486
495
  sampling_rate=1,
487
496
  )
488
- # frequency_mask = frequency_grid <= self.frequency_cutoff
489
497
 
490
498
  with np.errstate(divide="ignore"):
491
499
  np.power(frequency_grid, power, out=frequency_grid)
@@ -498,10 +506,7 @@ class Wedge:
498
506
  out=frequency_grid,
499
507
  )
500
508
 
501
- np.exp(frequency_grid, out=frequency_grid)
502
- # np.multiply(frequency_grid, frequency_mask, out=frequency_grid)
503
-
504
- wedges[index] = frequency_grid
509
+ wedges[index] = np.exp(frequency_grid)
505
510
 
506
511
  return wedges
507
512
 
@@ -510,14 +515,24 @@ class WedgeReconstructed:
510
515
  """
511
516
  Initialize :py:class:`WedgeReconstructed`.
512
517
 
513
- Parameters:
514
- -----------
515
- angles : Tuple[float], optional
518
+ Parameters
519
+ ----------
520
+ angles :tuple of float, optional
516
521
  The tilt angles, defaults to None.
517
522
  opening_axis : int, optional
518
523
  The axis around which the wedge is opened, defaults to 0.
519
524
  tilt_axis : int, optional
520
525
  The axis along which the tilt is applied, defaults to 2.
526
+ weights : tuple of float, optional
527
+ Weights to assign to individual wedge components.
528
+ weight_wedge : bool, optional
529
+ Whether individual wedge components should be weighted. If True and weights
530
+ is None, uses the cosine of the angle otherwise weights.
531
+ create_continuous_wedge: bool, optional
532
+ Whether to create a continous wedge or a per-component wedge. Weights are only
533
+ considered for non-continuous wedges.
534
+ frequency_cutoff : float, optional
535
+ Filter window applied during reconstruction.
521
536
  **kwargs : Dict
522
537
  Additional keyword arguments.
523
538
  """
@@ -525,33 +540,37 @@ class WedgeReconstructed:
525
540
  def __init__(
526
541
  self,
527
542
  angles: Tuple[float] = None,
528
- start_tilt: float = None,
529
- stop_tilt: float = None,
530
543
  opening_axis: int = 0,
531
544
  tilt_axis: int = 2,
545
+ weights: Tuple[float] = None,
532
546
  weight_wedge: bool = False,
533
547
  create_continuous_wedge: bool = False,
548
+ frequency_cutoff: float = 0.5,
549
+ reconstruction_filter: str = None,
534
550
  **kwargs: Dict,
535
551
  ):
536
552
  self.angles = angles
537
553
  self.opening_axis = opening_axis
538
554
  self.tilt_axis = tilt_axis
555
+ self.weights = weights
539
556
  self.weight_wedge = weight_wedge
557
+ self.reconstruction_filter = reconstruction_filter
540
558
  self.create_continuous_wedge = create_continuous_wedge
559
+ self.frequency_cutoff = frequency_cutoff
541
560
 
542
561
  def __call__(self, shape: Tuple[int], **kwargs: Dict) -> Dict:
543
562
  """
544
563
  Generate the reconstructed wedge.
545
564
 
546
- Parameters:
547
- -----------
565
+ Parameters
566
+ ----------
548
567
  shape : tuple of int
549
568
  The shape of the reconstruction volume.
550
569
  **kwargs : Dict
551
570
  Additional keyword arguments.
552
571
 
553
- Returns:
554
- --------
572
+ Returns
573
+ -------
555
574
  Dict
556
575
  A dictionary containing the reconstructed wedge and related information.
557
576
  """
@@ -559,15 +578,39 @@ class WedgeReconstructed:
559
578
  func_args.update(kwargs)
560
579
 
561
580
  if kwargs.get("is_fourier_shape", False):
562
- print("Cannot create continuous wedge mask basde on real fourier shape.")
581
+ print("Cannot create continuous wedge mask based on real fourier shape.")
563
582
 
564
583
  func = self.step_wedge
565
584
  if func_args.get("create_continuous_wedge", False):
566
585
  func = self.continuous_wedge
567
586
 
587
+ weight_wedge = func_args.get("weight_wedge", False)
588
+ if func_args.get("wedge_weights") is None and weight_wedge:
589
+ func_args["weights"] = np.cos(
590
+ np.radians(be.to_numpy_array(func_args.get("angles", (0,))))
591
+ )
592
+
568
593
  ret = func(shape=shape, **func_args)
594
+
595
+ frequency_cutoff = func_args.get("frequency_cutoff", None)
596
+ if frequency_cutoff is not None:
597
+ frequency_mask = fftfreqn(
598
+ shape=shape,
599
+ sampling_rate=1,
600
+ compute_euclidean_norm=True,
601
+ shape_is_real_fourier=False,
602
+ )
603
+ ret = np.multiply(ret, frequency_mask <= frequency_cutoff, out=ret)
604
+
605
+ if not weight_wedge:
606
+ ret = (ret > 0) * 1.0
607
+
569
608
  ret = be.astype(be.to_backend_array(ret), be._float_dtype)
570
609
 
610
+ ret = shift_fourier(data=ret, shape_is_real_fourier=False)
611
+ if func_args.get("return_real_fourier", False):
612
+ ret = crop_real_fourier(ret)
613
+
571
614
  return {
572
615
  "data": ret,
573
616
  "shape_is_real_fourier": func_args["return_real_fourier"],
@@ -584,14 +627,13 @@ class WedgeReconstructed:
584
627
  angles: Tuple[float],
585
628
  opening_axis: int,
586
629
  tilt_axis: int,
587
- return_real_fourier: bool,
588
630
  **kwargs: Dict,
589
631
  ) -> NDArray:
590
632
  """
591
- Generate a continuous reconstructed wedge.
633
+ Generate a continous wedge mask with DC component at the center.
592
634
 
593
- Parameters:
594
- -----------
635
+ Parameters
636
+ ----------
595
637
  shape : tuple of int
596
638
  The shape of the reconstruction volume.
597
639
  angles : tuple of float
@@ -600,27 +642,28 @@ class WedgeReconstructed:
600
642
  The axis around which the wedge is opened.
601
643
  tilt_axis : int
602
644
  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
645
 
606
- Returns:
607
- --------
646
+ Returns
647
+ -------
608
648
  NDArray
609
- The reconstructed wedge.
649
+ Wedge mask.
610
650
  """
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,
651
+ start_radians = np.tan(np.radians(90 - angles[0]))
652
+ stop_radians = np.tan(np.radians(-1 * (90 - angles[1])))
653
+
654
+ grid = centered_grid(shape)
655
+ with np.errstate(divide="ignore", invalid="ignore"):
656
+ ratios = np.where(
657
+ grid[opening_axis] == 0,
658
+ np.tan(np.radians(90)) + 1,
659
+ grid[tilt_axis] / grid[opening_axis],
660
+ )
661
+
662
+ wedge = np.logical_or(start_radians <= ratios, stop_radians >= ratios).astype(
663
+ np.float32
621
664
  )
622
665
 
623
- return ret
666
+ return wedge
624
667
 
625
668
  @staticmethod
626
669
  def step_wedge(
@@ -628,15 +671,15 @@ class WedgeReconstructed:
628
671
  angles: Tuple[float],
629
672
  opening_axis: int,
630
673
  tilt_axis: int,
631
- return_real_fourier: bool,
632
- weight_wedge: bool = False,
674
+ weights: Tuple[float] = None,
675
+ reconstruction_filter: str = None,
633
676
  **kwargs: Dict,
634
677
  ) -> NDArray:
635
678
  """
636
- Generate a step-wise reconstructed wedge.
679
+ Generate a per-angle wedge shape with DC component at the center.
637
680
 
638
- Parameters:
639
- -----------
681
+ Parameters
682
+ ----------
640
683
  shape : tuple of int
641
684
  The shape of the reconstruction volume.
642
685
  angles : tuple of float
@@ -645,38 +688,76 @@ class WedgeReconstructed:
645
688
  The axis around which the wedge is opened.
646
689
  tilt_axis : int
647
690
  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.
691
+ reconstruction_filter : str
692
+ Filter used during reconstruction.
693
+ weights : tuple of float, optional
694
+ Weights to assign to individual tilts. Defaults to 1.
652
695
 
653
- Returns:
654
- --------
696
+ Returns
697
+ -------
655
698
  NDArray
656
- The reconstructed wedge.
699
+ Wege mask.
657
700
  """
658
- preprocessor = Preprocessor()
701
+ from ..backends import NumpyFFTWBackend
659
702
 
660
703
  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,
704
+
705
+ if weights is None:
706
+ weights = np.ones(angles.size)
707
+ weights = np.asarray(weights)
708
+
709
+ shape = tuple(int(x) for x in shape)
710
+ opening_axis, tilt_axis = int(opening_axis), int(tilt_axis)
711
+
712
+ weights = np.repeat(weights, angles.size // weights.size)
713
+ plane = np.zeros(
714
+ (shape[opening_axis], shape[tilt_axis] + (1 - shape[tilt_axis] % 2)),
715
+ dtype=np.float32,
674
716
  )
675
717
 
676
- if not weight_wedge:
677
- ret = (ret > 0) * 1.0
718
+ # plane = np.zeros((shape[opening_axis], int(2 * np.max(shape)) + 1), dtype=np.float32)
678
719
 
679
- return ret
720
+ rec_filter = 1
721
+ if reconstruction_filter is not None:
722
+ rec_filter = create_reconstruction_filter(
723
+ plane.shape[::-1], filter_type=reconstruction_filter, tilt_angles=angles
724
+ ).T
725
+
726
+ subset = tuple(
727
+ slice(None) if i != 0 else slice(x // 2, x // 2 + 1)
728
+ for i, x in enumerate(plane.shape)
729
+ )
730
+ plane_rotated, wedge_volume = np.zeros_like(plane), np.zeros_like(plane)
731
+ for index in range(angles.shape[0]):
732
+ plane_rotated.fill(0)
733
+ plane[subset] = 1
734
+ rotation_matrix = euler_to_rotationmatrix((angles[index], 0))
735
+ rotation_matrix = rotation_matrix[np.ix_((0, 1), (0, 1))]
736
+
737
+ NumpyFFTWBackend().rigid_transform(
738
+ arr=plane * rec_filter,
739
+ rotation_matrix=rotation_matrix,
740
+ out=plane_rotated,
741
+ use_geometric_center=True,
742
+ order=1,
743
+ )
744
+ wedge_volume += plane_rotated * weights[index]
745
+
746
+ wedge_volume = centered(wedge_volume, (shape[opening_axis], shape[tilt_axis]))
747
+ np.fmin(wedge_volume, np.max(weights), wedge_volume)
748
+
749
+ if opening_axis > tilt_axis:
750
+ wedge_volume = np.moveaxis(wedge_volume, 1, 0)
751
+
752
+ reshape_dimensions = tuple(
753
+ x if i in (opening_axis, tilt_axis) else 1 for i, x in enumerate(shape)
754
+ )
755
+
756
+ wedge_volume = wedge_volume.reshape(reshape_dimensions)
757
+ tile_dimensions = np.divide(shape, reshape_dimensions).astype(int)
758
+ wedge_volume = np.tile(wedge_volume, tile_dimensions)
759
+
760
+ return wedge_volume
680
761
 
681
762
 
682
763
  @dataclass
@@ -726,8 +807,8 @@ class CTF:
726
807
  """
727
808
  Initialize :py:class:`CTF` from file.
728
809
 
729
- Parameters:
730
- -----------
810
+ Parameters
811
+ ----------
731
812
  filename : str
732
813
  The path to a file with ctf parameters. Supports the following formats:
733
814
  - CTFFIND4
@@ -857,8 +938,8 @@ class CTF:
857
938
  """
858
939
  Compute the CTF weight tilt stack.
859
940
 
860
- Parameters:
861
- -----------
941
+ Parameters
942
+ ----------
862
943
  shape : tuple of int
863
944
  The shape of the CTF.
864
945
  defocus_x : tuple of float
@@ -890,8 +971,8 @@ class CTF:
890
971
  **kwargs : Dict
891
972
  Additional keyword arguments.
892
973
 
893
- Returns:
894
- --------
974
+ Returns
975
+ -------
895
976
  NDArray
896
977
  A stack containing the CTF weight.
897
978
  """