miss-alignment 0.1.4__tar.gz

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 (113) hide show
  1. miss_alignment-0.1.4/.github/ISSUE_TEMPLATE.md +15 -0
  2. miss_alignment-0.1.4/.github/TEST_FAIL_TEMPLATE.md +12 -0
  3. miss_alignment-0.1.4/.github/dependabot.yml +10 -0
  4. miss_alignment-0.1.4/.github/workflows/ci.yml +90 -0
  5. miss_alignment-0.1.4/.gitignore +114 -0
  6. miss_alignment-0.1.4/.pre-commit-config.yaml +15 -0
  7. miss_alignment-0.1.4/CLAUDE.md +346 -0
  8. miss_alignment-0.1.4/LICENSE +28 -0
  9. miss_alignment-0.1.4/PKG-INFO +90 -0
  10. miss_alignment-0.1.4/README.md +40 -0
  11. miss_alignment-0.1.4/docs/run_in_warptools.md +19 -0
  12. miss_alignment-0.1.4/examples/correlate_reconstruction.py +56 -0
  13. miss_alignment-0.1.4/examples/etomo_to_warp.py +291 -0
  14. miss_alignment-0.1.4/examples/plot_data.py +211 -0
  15. miss_alignment-0.1.4/examples/plot_shift_generation.py +119 -0
  16. miss_alignment-0.1.4/examples/postproc.py +71 -0
  17. miss_alignment-0.1.4/examples/profile_create_pool_reconstruction.py +41 -0
  18. miss_alignment-0.1.4/examples/shrec/_raw_shrec_to_pickles.py +124 -0
  19. miss_alignment-0.1.4/examples/shrec/at2-alignment.json +1282 -0
  20. miss_alignment-0.1.4/examples/shrec/at3-alignment.json +1154 -0
  21. miss_alignment-0.1.4/examples/shrec/compare_to_ground_truth.py +291 -0
  22. miss_alignment-0.1.4/examples/shrec/compare_to_ground_truth_aretomo.py +508 -0
  23. miss_alignment-0.1.4/examples/shrec/how-to-run.md +102 -0
  24. miss_alignment-0.1.4/examples/shrec/iterations/iter0.json +1282 -0
  25. miss_alignment-0.1.4/examples/shrec/iterations/iter1.json +1282 -0
  26. miss_alignment-0.1.4/examples/shrec/iterations/iter2.json +1282 -0
  27. miss_alignment-0.1.4/examples/shrec/iterations/iter3.json +1282 -0
  28. miss_alignment-0.1.4/examples/shrec/iterations/iter4.json +1282 -0
  29. miss_alignment-0.1.4/examples/shrec/iterations/iter5.json +1282 -0
  30. miss_alignment-0.1.4/examples/shrec/iterations/iter6.json +1282 -0
  31. miss_alignment-0.1.4/examples/shrec/plot_alignment_comparison.py +140 -0
  32. miss_alignment-0.1.4/examples/shrec/preproc.py +181 -0
  33. miss_alignment-0.1.4/examples/shrec/running_aretomo.md +70 -0
  34. miss_alignment-0.1.4/examples/shrec/shrec_experiment_downsample_errors_per_ts.json +1282 -0
  35. miss_alignment-0.1.4/examples/shrec/shrec_experiment_downsample_result_summary.txt +129 -0
  36. miss_alignment-0.1.4/paper/manuscript.md +28 -0
  37. miss_alignment-0.1.4/paper/notebooks/illustrate_shifts.ipynb +579 -0
  38. miss_alignment-0.1.4/paper/notebooks/shift_illustration_combined.svg +1161 -0
  39. miss_alignment-0.1.4/paper/notebooks/shift_illustration_fractures.svg +1225 -0
  40. miss_alignment-0.1.4/paper/notebooks/shift_illustration_jitter.svg +1088 -0
  41. miss_alignment-0.1.4/paper/notebooks/shift_illustration_no_shifts.svg +1167 -0
  42. miss_alignment-0.1.4/paper/notebooks/shift_illustration_outliers.svg +1168 -0
  43. miss_alignment-0.1.4/paper/notebooks/shift_illustration_trajectory.svg +1178 -0
  44. miss_alignment-0.1.4/paper/notebooks/shrec_eval/ablation_experiment.ipynb +161 -0
  45. miss_alignment-0.1.4/paper/notebooks/shrec_eval/alignment_progression.gif +0 -0
  46. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_0.aln +65 -0
  47. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_1.aln +65 -0
  48. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_2.aln +65 -0
  49. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_3.aln +65 -0
  50. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_4.aln +65 -0
  51. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_5.aln +65 -0
  52. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_6.aln +65 -0
  53. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_7.aln +65 -0
  54. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_8.aln +65 -0
  55. miss_alignment-0.1.4/paper/notebooks/shrec_eval/aretomo1_alignment/model_9.aln +65 -0
  56. miss_alignment-0.1.4/paper/notebooks/shrec_eval/model_2_baseline_xy.svg +44 -0
  57. miss_alignment-0.1.4/paper/notebooks/shrec_eval/model_2_baseline_xz.svg +44 -0
  58. miss_alignment-0.1.4/paper/notebooks/shrec_eval/model_2_baseline_yz.svg +44 -0
  59. miss_alignment-0.1.4/paper/notebooks/shrec_eval/model_2_missalignment_xy.svg +44 -0
  60. miss_alignment-0.1.4/paper/notebooks/shrec_eval/model_2_missalignment_xz.svg +44 -0
  61. miss_alignment-0.1.4/paper/notebooks/shrec_eval/model_2_missalignment_yz.svg +44 -0
  62. miss_alignment-0.1.4/paper/notebooks/shrec_eval/shrec_benchmark_per_tilt_error.svg +1331 -0
  63. miss_alignment-0.1.4/paper/notebooks/shrec_eval/shrec_benchmark_progression_combined_error.svg +1555 -0
  64. miss_alignment-0.1.4/paper/notebooks/shrec_eval/utils.py +32 -0
  65. miss_alignment-0.1.4/paper/notebooks/shrec_eval/visualize_iterations.ipynb +760 -0
  66. miss_alignment-0.1.4/paper/notebooks/shrec_eval/visualize_reconstruction_gif.ipynb +438 -0
  67. miss_alignment-0.1.4/paper/notebooks/shrec_eval/visualize_reconstruction_slices.ipynb +461 -0
  68. miss_alignment-0.1.4/paper/notebooks/triplet_clone.mrc +0 -0
  69. miss_alignment-0.1.4/paper/notebooks/triplet_degraded.mrc +0 -0
  70. miss_alignment-0.1.4/paper/notebooks/triplet_perfect.mrc +0 -0
  71. miss_alignment-0.1.4/paper/notebooks/view_triplet.py +12 -0
  72. miss_alignment-0.1.4/pyproject.toml +128 -0
  73. miss_alignment-0.1.4/src/miss_alignment/__init__.py +19 -0
  74. miss_alignment-0.1.4/src/miss_alignment/_cli.py +13 -0
  75. miss_alignment-0.1.4/src/miss_alignment/alignment/__init__.py +4 -0
  76. miss_alignment-0.1.4/src/miss_alignment/alignment/correlation.py +112 -0
  77. miss_alignment-0.1.4/src/miss_alignment/alignment/optimize_global.py +519 -0
  78. miss_alignment-0.1.4/src/miss_alignment/alignment/optimize_iterative.py +248 -0
  79. miss_alignment-0.1.4/src/miss_alignment/alignment/optimize_spline.py +404 -0
  80. miss_alignment-0.1.4/src/miss_alignment/alignment/parallel.py +158 -0
  81. miss_alignment-0.1.4/src/miss_alignment/alignment/statistics.py +245 -0
  82. miss_alignment-0.1.4/src/miss_alignment/alignment/tilt_series.py +266 -0
  83. miss_alignment-0.1.4/src/miss_alignment/alignment/utils.py +32 -0
  84. miss_alignment-0.1.4/src/miss_alignment/config_template.yaml +51 -0
  85. miss_alignment-0.1.4/src/miss_alignment/data/__init__.py +5 -0
  86. miss_alignment-0.1.4/src/miss_alignment/data/_augmentation.py +110 -0
  87. miss_alignment-0.1.4/src/miss_alignment/data/_reconstruction_worker.py +422 -0
  88. miss_alignment-0.1.4/src/miss_alignment/data/io.py +210 -0
  89. miss_alignment-0.1.4/src/miss_alignment/data/shift_generation.py +353 -0
  90. miss_alignment-0.1.4/src/miss_alignment/data/training_datamodule.py +301 -0
  91. miss_alignment-0.1.4/src/miss_alignment/data/training_dataset.py +150 -0
  92. miss_alignment-0.1.4/src/miss_alignment/gradcam/__init__.py +0 -0
  93. miss_alignment-0.1.4/src/miss_alignment/gradcam/gradcam.py +123 -0
  94. miss_alignment-0.1.4/src/miss_alignment/models/__init__.py +31 -0
  95. miss_alignment-0.1.4/src/miss_alignment/models/_compact.py +364 -0
  96. miss_alignment-0.1.4/src/miss_alignment/models/_resnet.py +209 -0
  97. miss_alignment-0.1.4/src/miss_alignment/models/models.py +523 -0
  98. miss_alignment-0.1.4/src/miss_alignment/prepare_stacks.py +167 -0
  99. miss_alignment-0.1.4/src/miss_alignment/preprocessing.py +151 -0
  100. miss_alignment-0.1.4/src/miss_alignment/py.typed +5 -0
  101. miss_alignment-0.1.4/src/miss_alignment/train.py +337 -0
  102. miss_alignment-0.1.4/src/miss_alignment/utils.py +51 -0
  103. miss_alignment-0.1.4/tests/alignment/test_optimize_global.py +193 -0
  104. miss_alignment-0.1.4/tests/alignment/test_statistics.py +244 -0
  105. miss_alignment-0.1.4/tests/alignment/test_tilt_series.py +330 -0
  106. miss_alignment-0.1.4/tests/data/test_augmentation.py +386 -0
  107. miss_alignment-0.1.4/tests/data/test_io.py +215 -0
  108. miss_alignment-0.1.4/tests/data/test_pool_dataset.py +278 -0
  109. miss_alignment-0.1.4/tests/data/test_reconstruction_worker.py +779 -0
  110. miss_alignment-0.1.4/tests/data/test_shift_generation.py +553 -0
  111. miss_alignment-0.1.4/tests/models/test_models.py +1 -0
  112. miss_alignment-0.1.4/tests/models/test_resnet.py +15 -0
  113. miss_alignment-0.1.4/tests/test_miss_alignment.py +2 -0
@@ -0,0 +1,15 @@
1
+ * miss-alignment version:
2
+ * Python version:
3
+ * Operating System:
4
+
5
+ ### Description
6
+
7
+ Describe what you were trying to get done.
8
+ Tell us what happened, what went wrong, and what you expected to happen.
9
+
10
+ ### What I Did
11
+
12
+ ```
13
+ Paste the command(s) you ran and the output.
14
+ If there was a crash, please include the traceback here.
15
+ ```
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: "{{ env.TITLE }}"
3
+ labels: [bug]
4
+ ---
5
+ The {{ workflow }} workflow failed on {{ date | date("YYYY-MM-DD HH:mm") }} UTC
6
+
7
+ The most recent failing test was on {{ env.PLATFORM }} py{{ env.PYTHON }}
8
+ with commit: {{ sha }}
9
+
10
+ Full run: https://github.com/{{ repo }}/actions/runs/{{ env.RUN_ID }}
11
+
12
+ (This post will be updated if another test fails, as long as this issue remains open.)
@@ -0,0 +1,10 @@
1
+ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
2
+
3
+ version: 2
4
+ updates:
5
+ - package-ecosystem: "github-actions"
6
+ directory: "/"
7
+ schedule:
8
+ interval: "weekly"
9
+ commit-message:
10
+ prefix: "ci(dependabot):"
@@ -0,0 +1,90 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ tags:
8
+ - "v*"
9
+ pull_request:
10
+ workflow_dispatch:
11
+
12
+
13
+ # cancel in-progress runs that use the same workflow and branch
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ test:
20
+ name: ${{ matrix.platform }} (${{ matrix.python-version }})
21
+ runs-on: ${{ matrix.platform }}
22
+ strategy:
23
+ fail-fast: false
24
+ matrix:
25
+ python-version: ["3.10", "3.13"]
26
+ platform: [ubuntu-latest]
27
+
28
+ steps:
29
+ - uses: actions/checkout@v6
30
+
31
+ - name: 🐍 Set up Python ${{ matrix.python-version }}
32
+ uses: actions/setup-python@v6
33
+ with:
34
+ python-version: ${{ matrix.python-version }}
35
+ cache-dependency-path: "pyproject.toml"
36
+ cache: "pip"
37
+
38
+ - name: Install PyTorch
39
+ run: |
40
+ python -m pip install -U pip
41
+ python -m pip install torch==2.6.0 --index-url https://download.pytorch.org/whl/cpu
42
+
43
+ - name: Install torch-projectors (CPU)
44
+ run: |
45
+ python -m pip install torch-projectors --index-url https://warpem.github.io/torch-projectors/cpu/simple/
46
+
47
+ - name: Install package
48
+ run: python -m pip install .[test]
49
+
50
+ - name: 🧪 Run Tests
51
+ run: pytest --color=yes --cov --cov-report=xml --cov-report=term-missing
52
+
53
+ - name: Coverage
54
+ uses: codecov/codecov-action@v6
55
+
56
+ deploy:
57
+ name: Deploy
58
+ needs: test
59
+ if: success() && startsWith(github.ref, 'refs/tags/')
60
+ runs-on: ubuntu-latest
61
+
62
+ permissions:
63
+ # IMPORTANT: this permission is mandatory for trusted publishing on PyPi
64
+ # see https://docs.pypi.org/trusted-publishers/
65
+ id-token: write
66
+ # This permission allows writing releases
67
+ contents: write
68
+
69
+ steps:
70
+ - uses: actions/checkout@v6
71
+ with:
72
+ fetch-depth: 0
73
+
74
+ - name: 🐍 Set up Python
75
+ uses: actions/setup-python@v6
76
+ with:
77
+ python-version: "3.x"
78
+
79
+ - name: 👷 Build
80
+ run: |
81
+ python -m pip install build
82
+ python -m build
83
+
84
+ - name: 🚢 Publish to PyPI
85
+ uses: pypa/gh-action-pypi-publish@release/v1
86
+
87
+ - uses: softprops/action-gh-release@v3
88
+ with:
89
+ generate_release_notes: true
90
+ files: './dist/*'
@@ -0,0 +1,114 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ env/
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+
28
+ .DS_Store
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ .hypothesis/
50
+ .pytest_cache/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+
60
+ # Flask stuff:
61
+ instance/
62
+ .webassets-cache
63
+
64
+ # Scrapy stuff:
65
+ .scrapy
66
+
67
+ # Sphinx documentation
68
+ docs/_build/
69
+
70
+ # PyBuilder
71
+ target/
72
+
73
+ # Jupyter Notebook
74
+ .ipynb_checkpoints
75
+
76
+ # pyenv
77
+ .python-version
78
+
79
+ # celery beat schedule file
80
+ celerybeat-schedule
81
+
82
+ # SageMath parsed files
83
+ *.sage.py
84
+
85
+ # dotenv
86
+ .env
87
+
88
+ # virtualenv
89
+ .venv
90
+ venv/
91
+ ENV/
92
+
93
+ # Spyder project settings
94
+ .spyderproject
95
+ .spyproject
96
+
97
+ # Rope project settings
98
+ .ropeproject
99
+
100
+ # mkdocs documentation
101
+ /site
102
+
103
+ # mypy
104
+ .mypy_cache/
105
+
106
+ # ruff
107
+ .ruff_cache/
108
+
109
+ # IDE settings
110
+ .vscode/
111
+ .idea/
112
+
113
+ # pydev copier answers
114
+ .copier-answers.yml
@@ -0,0 +1,15 @@
1
+ files: >
2
+ (?x)^(
3
+ src/.*|
4
+ tests/.*
5
+ )$
6
+ repos:
7
+ - repo: https://github.com/astral-sh/ruff-pre-commit
8
+ # Ruff version.
9
+ rev: v0.9.9
10
+ hooks:
11
+ # Run the linter.
12
+ - id: ruff
13
+ args: [ --fix ]
14
+ # Run the formatter.
15
+ - id: ruff-format
@@ -0,0 +1,346 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ miss-alignment is a deep learning package for tilt-series alignment in cryo-electron tomography. It uses a contrastive learning approach with PyTorch and PyTorch Lightning to iteratively train 3D convolutional neural networks that optimize tilt-series alignment by minimizing shift artifacts in reconstructions.
8
+
9
+ **Key concept**: The system alternates between (1) training a model to score reconstruction quality and (2) using that model to optimize tilt-series alignment parameters through gradient descent.
10
+
11
+ ### Scientific publication
12
+
13
+ This software is a research project. It will be written up in a manuscript. Therefore, consider for anything you write in the paper/ folder that it should adhere to scientific principles with an emphasis on clarity and simplicity.
14
+
15
+ ## Environment Setup
16
+
17
+ This project has specific CUDA and PyTorch dependencies. Use the conda environment setup from README.md:
18
+
19
+ ```bash
20
+ conda create -n miss-alignment -c conda-forge python=3.11 cuda-toolkit=12.9 -y
21
+ conda activate miss-alignment
22
+ ```
23
+
24
+ Install with: `python -m pip install -e .[dev,test]`
25
+
26
+ ## Common Commands
27
+
28
+ ### Testing
29
+ ```bash
30
+ # Run all tests with coverage
31
+ pytest --color=yes --cov --cov-report=xml --cov-report=term-missing
32
+
33
+ # Run tests in a specific directory
34
+ pytest tests/alignment/
35
+
36
+ # Run a single test file
37
+ pytest tests/data/test_training_dataset.py
38
+
39
+ # Run a specific test function
40
+ pytest tests/test_miss_alignment.py::test_something
41
+ ```
42
+
43
+ ### Linting
44
+ ```bash
45
+ # Run ruff linter with auto-fix
46
+ ruff check --fix
47
+
48
+ # Run ruff formatter
49
+ ruff format
50
+
51
+ # Run pre-commit hooks manually
52
+ pre-commit run --all-files
53
+ ```
54
+
55
+ ### CLI Usage
56
+ The package provides a `miss-alignment` CLI command:
57
+ ```bash
58
+ # Train a model (primary workflow)
59
+ miss-alignment --config-file config.yaml
60
+ ```
61
+
62
+ ### Development
63
+ ```bash
64
+ # Build the package
65
+ python -m build
66
+
67
+ # Install in editable mode with dependencies
68
+ python -m pip install -e .[dev,test]
69
+ ```
70
+
71
+ ## Architecture
72
+
73
+ ### Core Modules
74
+
75
+ 1. **`models/`** - Neural network architectures
76
+ - `MissAlignment`: Main PyTorch Lightning module that wraps the 3D CNN
77
+ - Model variants: `Compact3DConvNet` (default), `Compact3DConvNetGELU`, `Compact3DConvNetWide`, `Compact3DConvNetDeep`, `CompactResNet3D`
78
+ - Uses `TripletMarginRankingLoss` for contrastive learning (comparing aligned vs. misaligned reconstructions)
79
+
80
+ 2. **`data/`** - Data loading and synthetic shift generation
81
+ - `MissAlignmentDataModule`: PyTorch Lightning data module managing the reconstruction pool
82
+ - `ReconstructionPoolDataset`: Consumes from a pool of pre-computed 3D patches
83
+ - `shift_generation.py`: Creates synthetic alignment errors (trajectories, jitter, outliers, fractures) for training data
84
+ - `_reconstruction_worker.py`: Multiprocessing workers that generate 3D reconstruction patches in parallel
85
+ - **Architecture note**: Uses a producer-consumer pattern where reconstruction workers populate a temporary pool directory that the dataloader consumes from
86
+
87
+ 3. **`alignment/`** - Tilt-series alignment optimization
88
+ - `tilt_series.py`: Core optimization logic using gradient descent on shift parameters
89
+ - `parallel.py`: Distributes alignment across multiple GPU devices
90
+ - `correlation.py`: Traditional correlation-based alignment methods
91
+ - **Key functions**: `optimize_shifts()` supports three modes:
92
+ - `"global"`: Single shift per image
93
+ - `(3, 3, 41)`: 2D warping field per image
94
+ - `(3, 3, 2, 10)`: 3D volume warp grid
95
+
96
+ 4. **`train.py`** - Iterative training loop
97
+ - Alternates between model training and tilt-series alignment
98
+ - Each iteration: train model → align tilt-series → use aligned data for next iteration
99
+ - Configured via YAML file (see `config_template.yaml`)
100
+
101
+ ### Dependencies on External Libraries
102
+
103
+ - **`warpylib`**: Provides `TiltSeries` and `CubicGrid` for tilt-series geometry and warping
104
+ - **`torch-fourier-*`**: Suite of PyTorch-based Fourier transform utilities (rescale, slice, shift, filter)
105
+ - **`torch-tiltxcorr`**: Cross-correlation utilities for tilt-series
106
+ - **`torch-grid-utils`** and **`torch-cubic-spline-grids`**: Grid manipulation utilities
107
+
108
+ ### Configuration System
109
+
110
+ Training is configured via YAML files (template: `src/miss_alignment/config_template.yaml`):
111
+ - **`general`**: Training directory, CTF settings, iteration-specific parameters (downsample, alignment mode)
112
+ - **`model_training`**: Architecture selection, learning rate, loss margin, weight decay, scheduler
113
+ - **`data_loading`**: Batch size, patch size, steps per epoch
114
+ - **`shift_generation`**: Probabilities and magnitudes for synthetic shifts (trajectories, jitter, outliers, fractures)
115
+ - **`tilt_series_alignment`**: Patch size, overlap, batch size for alignment
116
+
117
+ ### Data Flow
118
+
119
+ 1. **Training**: Tilt-series XML files (`.xml`) → Reconstruction workers → Pool directory (`.pickle` patches triplet) → DataLoader → Model
120
+ 2. **Alignment**: Trained model checkpoint → Load tilt-series data → Gradient-based optimization → Output aligned parameters to XML files (`.xml`)
121
+
122
+ ## Working with Tilt-Series Data
123
+
124
+ This project uses `warpylib.TiltSeries` as the primary representation of tilt-series metadata. Tilt-series data is stored in `.xml` files that contain:
125
+ - Alignment parameters (tilt angles, tilt axis angles, shifts)
126
+ - Volume dimensions (physical dimensions in Angstroms)
127
+ - Image dimensions (physical dimensions in Angstroms)
128
+ - Path to the image stack (`.st` or `.mrc` files)
129
+
130
+ **Note**: `torch-tomogram` is a dependency only for backward compatibility. Use `warpylib` for all tilt-series operations.
131
+
132
+ ### Directory Structure Convention
133
+
134
+ warpylib expects a specific directory structure for tilt-series data. When you have an XML metadata file, the corresponding image stack is located in a subdirectory structure:
135
+
136
+ ```
137
+ /path/to/data/
138
+ ├── tilt_series_01.xml # XML metadata file
139
+ ├── tilt_series_02.xml
140
+ └── tiltstack/ # Stack directory
141
+ ├── tilt_series_01/ # Subdirectory per tilt-series
142
+ │ ├── tilt_series_01.st # Image stack
143
+ │ └── tilt_series_01.rawtlt # Optional angle file
144
+ └── tilt_series_02/
145
+ ├── tilt_series_02.st
146
+ └── tilt_series_02.rawtlt
147
+ ```
148
+
149
+ The stack path is automatically derived from the XML path via the `tilt_stack_path` property:
150
+ - XML: `/path/to/data/series.xml`
151
+ - Stack: `/path/to/data/tiltstack/series/series.st`
152
+
153
+ This structure is automatically created when using `convert_pickle_to_xml_helper()` and is expected when loading data with `TiltSeriesData.load_metadata_and_stack()`.
154
+
155
+ ### Loading Tilt-Series Data
156
+
157
+ ```python
158
+ from pathlib import Path
159
+ from miss_alignment.data.io import TiltSeriesData
160
+
161
+ # Create TiltSeriesData directly from XML file path
162
+ tilt_series_data = TiltSeriesData(xml_metadata_path=Path("path/to/data.xml"))
163
+
164
+ # Load the TiltSeries object, images, and pixel size
165
+ # The XML file contains volume dimensions, image dimensions, and the path to the stack
166
+ tilt_series, images, pixel_size = tilt_series_data.load_metadata_and_stack(
167
+ downsample=1 # optional downsampling factor
168
+ )
169
+
170
+ # Access tilt-series metadata (all torch tensors)
171
+ angles = tilt_series.angles # tilt angles in degrees
172
+ tilt_axis_angles = tilt_series.tilt_axis_angles # tilt axis rotation per image
173
+ tilt_axis_offset_x = tilt_series.tilt_axis_offset_x # X shifts in Angstroms
174
+ tilt_axis_offset_y = tilt_series.tilt_axis_offset_y # Y shifts in Angstroms
175
+ ```
176
+
177
+ Alternatively, you can load directly with `warpylib`:
178
+
179
+ ```python
180
+ from pathlib import Path
181
+ from warpylib import TiltSeries
182
+
183
+ # Load warpylib TiltSeries directly from XML file
184
+ tilt_series = TiltSeries(Path("path/to/data.xml"))
185
+ ```
186
+
187
+ ### TiltSeries Key Attributes
188
+
189
+ The `warpylib.TiltSeries` object contains:
190
+ - **`angles`**: Tilt angles in degrees (shape: `[n_tilts]`)
191
+ - **`tilt_axis_angles`**: Rotation of tilt axis for each image in degrees (shape: `[n_tilts]`)
192
+ - **`tilt_axis_offset_x`**: X-axis shifts in Angstroms (shape: `[n_tilts]`)
193
+ - **`tilt_axis_offset_y`**: Y-axis shifts in Angstroms (shape: `[n_tilts]`)
194
+ - **`image_dimensions_physical`**: Physical image dimensions in Angstroms (shape: `[2]`)
195
+ - **`volume_dimensions_physical`**: Physical volume dimensions in Angstroms (shape: `[3]`)
196
+
197
+ All attributes are `torch.Tensor` objects.
198
+
199
+ ### Performing Reconstructions
200
+
201
+ ```python
202
+ import torch
203
+ from warpylib.tilt_series.reconstruct_volume import preprocess_tilt_data
204
+
205
+ # Preprocess images (normalize, optionally invert)
206
+ images = preprocess_tilt_data(
207
+ tilt_data=images,
208
+ normalize=True,
209
+ invert=False,
210
+ subvolume_size=64, # patch size for filter calculation
211
+ )
212
+
213
+ # Define reconstruction position (in Angstroms, in volume coordinate system)
214
+ reconstruction_location = torch.tensor([[x, y, z]], device="cuda")
215
+
216
+ # Perform reconstruction at a specific location
217
+ patch_size = 64 # size of output volume (cubic)
218
+ reconstruction = tilt_series.reconstruct_subvolumes_single(
219
+ tilt_data=images,
220
+ coords=reconstruction_location, # shape: (1, 3) in Angstroms
221
+ pixel_size=pixel_size,
222
+ size=patch_size,
223
+ apply_ctf=True, # apply CTF correction
224
+ angles=torch.tensor([0.0, 0.0, 0.0]), # optional rotation (ZYZ Euler)
225
+ oversampling=2.0, # reconstruction oversampling factor
226
+ )
227
+ # Output shape: (1, patch_size, patch_size, patch_size)
228
+ ```
229
+
230
+ ### Modifying Alignment Parameters
231
+
232
+ ```python
233
+ # Modify shifts (all operations in Angstroms)
234
+ tilt_series.tilt_axis_offset_x += shift_x # add X shifts
235
+ tilt_series.tilt_axis_offset_y += shift_y # add Y shifts
236
+
237
+ # Modify angles
238
+ tilt_series.angles = new_angles # set new tilt angles
239
+ tilt_series.tilt_axis_angles = new_tilt_axis_angles # set tilt axis rotation
240
+ ```
241
+
242
+ ### Saving Updated Metadata
243
+
244
+ ```python
245
+ # Save updated TiltSeries metadata back to the original XML file
246
+ tilt_series_data.save_metadata_to_xml(tilt_series)
247
+
248
+ # Or save to a new XML file
249
+ new_tilt_series_data = tilt_series_data.replace(
250
+ xml_metadata_path=Path("path/to/new_metadata.xml")
251
+ )
252
+ new_tilt_series_data.save_metadata_to_xml(tilt_series)
253
+
254
+ # Alternatively, save directly with warpylib
255
+ tilt_series.save_meta(Path("path/to/metadata.xml"))
256
+ ```
257
+
258
+ ### Adding Synthetic Shifts for Testing
259
+
260
+ ```python
261
+ from miss_alignment.data.shift_generation import (
262
+ JitterGenerator,
263
+ TrajectoryGenerator,
264
+ OutlierGenerator,
265
+ FractureGenerator,
266
+ )
267
+
268
+ # Create shift generators
269
+ n_tilts = len(tilt_series.angles)
270
+ device = tilt_series.angles.device
271
+
272
+ # Generate shifts (returned in pixels, shape: [n_tilts, 3] for ZYX)
273
+ jitter_shifts = JitterGenerator(jitter_max_std=2.0)(n_tilts, device)
274
+ trajectory_shifts = TrajectoryGenerator(trajectory_max_shift=10.0)(n_tilts, device)
275
+ outlier_shifts = OutlierGenerator(outlier_max_shift=20, max_sequence_length=3)(n_tilts, device)
276
+ fracture_shifts = FractureGenerator(fracture_max_shift=30)(n_tilts, device)
277
+
278
+ # Convert to 2D shifts and apply (need projection matrices)
279
+ from miss_alignment.data.shift_generation import project_shifts_3d_to_2d
280
+ from torch_affine_utils.transforms_3d import Ry, Rz
281
+
282
+ r0 = Ry(-tilt_series.angles, zyx=True)
283
+ r1 = Rz(tilt_series.tilt_axis_angles, zyx=True)
284
+ rotation_matrices = r1 @ r0
285
+ projection_matrices = rotation_matrices[..., 1:3, :3]
286
+
287
+ # Project 3D shifts to 2D (shape: [n_tilts, 2] for YX)
288
+ shifts_2d = project_shifts_3d_to_2d(jitter_shifts, projection_matrices)
289
+ shifts_angstrom = shifts_2d * pixel_size
290
+
291
+ # Apply shifts
292
+ tilt_series.tilt_axis_offset_y += shifts_angstrom[:, 0]
293
+ tilt_series.tilt_axis_offset_x += shifts_angstrom[:, 1]
294
+ ```
295
+
296
+ ## Development Notes
297
+
298
+ - **Python version**: Requires Python ≥3.10 (tested primarily on 3.11)
299
+ - **Linting**: Uses `ruff` with line length 88, ignores E712 (comparisons with False for readability)
300
+ - **Pre-commit**: Configured with ruff linter and formatter
301
+ - **Testing**: Uses `pytest` with coverage reporting; warnings treated as errors
302
+ - **Multiprocessing**: Reconstruction workers use multiprocessing with device assignment via `reconstruction_accelerators` parameter
303
+ - **Temporary files**: `MissAlignmentDataModule` creates a temporary pool directory (`RECON_POOL_SIZE` env var controls size, default 1000)
304
+ - **Package manager**: Currently uses conda/pip for environment installation -> would like to move to uv in the future
305
+
306
+ ## Bug Reporting and Fixes
307
+
308
+ When working on this codebase, if you encounter potential bugs or implementation issues:
309
+
310
+ 1. **Report the issue**: Clearly explain what you found and why it appears to be a bug
311
+ 2. **Suggest a fix**: Provide a proposed solution with reasoning
312
+ 3. **Implement if appropriate**: For clear bugs (e.g., missing conversions, type inconsistencies, logic errors), implement the fix
313
+ 4. **Let the maintainer decide**: The maintainer will evaluate whether it's truly a bug or was intentional design
314
+
315
+ **Examples of bugs to report and fix:**
316
+ - Missing data type conversions (e.g., forgetting to convert a list back to tuple in deserialization)
317
+ - Inconsistent behavior between related functions (e.g., `to_dict()` and `from_dict()` not being symmetric)
318
+ - Logic errors or off-by-one errors
319
+ - Missing error handling
320
+
321
+ **Don't just write tests to conform to buggy behavior** - if the implementation looks wrong, report it and suggest the fix rather than masking it with adjusted test expectations.
322
+
323
+ ## File Organization
324
+
325
+ ```
326
+ miss-alignment/
327
+ ├── src/miss_alignment/
328
+ │ ├── models/ # Neural network architectures
329
+ │ ├── data/ # Data loading, shift generation, reconstruction workers
330
+ │ ├── alignment/ # Alignment optimization algorithms
331
+ │ ├── gradcam/ # Gradient-weighted Class Activation Mapping utilities
332
+ │ ├── train.py # Main training loop
333
+ │ ├── _cli.py # CLI setup with typer
334
+ │ └── config_template.yaml # Configuration template
335
+ ├── tests/ # Test suite (mirrors src structure)
336
+ ├── examples/ # Example scripts for data processing and visualization
337
+ └── paper/ # Paper-related notebooks and evaluation
338
+ ```
339
+
340
+ ## Important Patterns
341
+
342
+ 1. **Configuration-driven**: All training parameters are specified in YAML files
343
+ 2. **Iterative refinement**: Training alternates between model training and alignment steps
344
+ 3. **Multi-GPU support**: Both training and alignment can use multiple GPUs
345
+ 4. **Synthetic data augmentation**: Training uses procedurally generated alignment errors
346
+ 5. **Pool-based data loading**: Pre-computed reconstructions in a pool improve training throughput
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026 Genentech, Inc. and Hubrecht Institute
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.