meteor-maps 0.2.2__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 (61) hide show
  1. meteor_maps-0.2.2/.github/workflows/deploy.yml +46 -0
  2. meteor_maps-0.2.2/.github/workflows/lint.yml +39 -0
  3. meteor_maps-0.2.2/.github/workflows/mypy.yml +35 -0
  4. meteor_maps-0.2.2/.github/workflows/tests.yml +55 -0
  5. meteor_maps-0.2.2/.gitignore +171 -0
  6. meteor_maps-0.2.2/LICENSE +21 -0
  7. meteor_maps-0.2.2/PKG-INFO +17 -0
  8. meteor_maps-0.2.2/README.md +58 -0
  9. meteor_maps-0.2.2/meteor/__init__.py +3 -0
  10. meteor_maps-0.2.2/meteor/_version.py +16 -0
  11. meteor_maps-0.2.2/meteor/diffmaps.py +183 -0
  12. meteor_maps-0.2.2/meteor/io.py +62 -0
  13. meteor_maps-0.2.2/meteor/iterative.py +259 -0
  14. meteor_maps-0.2.2/meteor/rsmap.py +430 -0
  15. meteor_maps-0.2.2/meteor/scale.py +209 -0
  16. meteor_maps-0.2.2/meteor/scripts/__init__.py +0 -0
  17. meteor_maps-0.2.2/meteor/scripts/common.py +335 -0
  18. meteor_maps-0.2.2/meteor/scripts/compute_difference_map.py +168 -0
  19. meteor_maps-0.2.2/meteor/scripts/compute_iterative_tv_map.py +113 -0
  20. meteor_maps-0.2.2/meteor/settings.py +49 -0
  21. meteor_maps-0.2.2/meteor/sfcalc.py +29 -0
  22. meteor_maps-0.2.2/meteor/testing.py +78 -0
  23. meteor_maps-0.2.2/meteor/tv.py +213 -0
  24. meteor_maps-0.2.2/meteor/utils.py +167 -0
  25. meteor_maps-0.2.2/meteor/validate.py +163 -0
  26. meteor_maps-0.2.2/meteor_maps.egg-info/PKG-INFO +17 -0
  27. meteor_maps-0.2.2/meteor_maps.egg-info/SOURCES.txt +59 -0
  28. meteor_maps-0.2.2/meteor_maps.egg-info/dependency_links.txt +1 -0
  29. meteor_maps-0.2.2/meteor_maps.egg-info/entry_points.txt +3 -0
  30. meteor_maps-0.2.2/meteor_maps.egg-info/requires.txt +11 -0
  31. meteor_maps-0.2.2/meteor_maps.egg-info/top_level.txt +1 -0
  32. meteor_maps-0.2.2/pyproject.toml +107 -0
  33. meteor_maps-0.2.2/setup.cfg +4 -0
  34. meteor_maps-0.2.2/test/__init__.py +0 -0
  35. meteor_maps-0.2.2/test/conftest.py +27 -0
  36. meteor_maps-0.2.2/test/data/8a6g-chromophore-removed.cif +2543 -0
  37. meteor_maps-0.2.2/test/data/8a6g-chromophore-removed.pdb +2439 -0
  38. meteor_maps-0.2.2/test/data/8a6g.pdb +2712 -0
  39. meteor_maps-0.2.2/test/data/README.md +47 -0
  40. meteor_maps-0.2.2/test/data/scaled-test-data.mtz +0 -0
  41. meteor_maps-0.2.2/test/functional/__init__.py +0 -0
  42. meteor_maps-0.2.2/test/functional/test_compute_difference_map.py +114 -0
  43. meteor_maps-0.2.2/test/functional/test_compute_iterative_tv_map.py +68 -0
  44. meteor_maps-0.2.2/test/functional/test_scale.py +38 -0
  45. meteor_maps-0.2.2/test/unit/__init__.py +0 -0
  46. meteor_maps-0.2.2/test/unit/conftest.py +109 -0
  47. meteor_maps-0.2.2/test/unit/scripts/__init__.py +0 -0
  48. meteor_maps-0.2.2/test/unit/scripts/conftest.py +42 -0
  49. meteor_maps-0.2.2/test/unit/scripts/test_common.py +176 -0
  50. meteor_maps-0.2.2/test/unit/scripts/test_compute_difference_map.py +72 -0
  51. meteor_maps-0.2.2/test/unit/scripts/test_compute_iterative_tv_map.py +122 -0
  52. meteor_maps-0.2.2/test/unit/test_diffmaps.py +165 -0
  53. meteor_maps-0.2.2/test/unit/test_io.py +105 -0
  54. meteor_maps-0.2.2/test/unit/test_iterative.py +120 -0
  55. meteor_maps-0.2.2/test/unit/test_rsmap.py +356 -0
  56. meteor_maps-0.2.2/test/unit/test_scale.py +125 -0
  57. meteor_maps-0.2.2/test/unit/test_sfcalc.py +70 -0
  58. meteor_maps-0.2.2/test/unit/test_testing.py +41 -0
  59. meteor_maps-0.2.2/test/unit/test_tv.py +123 -0
  60. meteor_maps-0.2.2/test/unit/test_utils.py +129 -0
  61. meteor_maps-0.2.2/test/unit/test_validate.py +53 -0
@@ -0,0 +1,46 @@
1
+ name: deploy
2
+
3
+ on:
4
+ workflow_run:
5
+ branches:
6
+ - master
7
+ workflows:
8
+ - "tests"
9
+ - "mypy"
10
+ - "ruff"
11
+ types:
12
+ - completed
13
+
14
+ jobs:
15
+ deploy:
16
+ name: deploy-to-pypi
17
+ runs-on: ubuntu-latest
18
+ # only run if previous workflows were a success
19
+ # AND the commit starts with v (meaning it's a tag)
20
+ if: ${{ github.event.workflow_run.conclusion == 'success' && startsWith(github.event.workflow_run.head_commit.message, 'v') }}
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.x"
29
+
30
+ - name: install
31
+ run: |
32
+ git tag
33
+ pip install -U pip
34
+ pip install -U build twine
35
+ python -m build
36
+ twine check dist/*
37
+ ls -lh dist
38
+ - name: build and publish
39
+ run: twine upload dist/*
40
+ env:
41
+ TWINE_USERNAME: __token__
42
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_KEY }}
43
+
44
+ - uses: softprops/action-gh-release@v2
45
+ with:
46
+ generate_release_notes: true
@@ -0,0 +1,39 @@
1
+ name: ruff
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python-version: ["3.11"]
11
+
12
+ steps:
13
+ - name: Cancel Previous Runs
14
+ uses: styfle/cancel-workflow-action@0.12.1
15
+ with:
16
+ access_token: ${{ github.token }}
17
+
18
+ - name: Checkout
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+
30
+ - name: Lint with Ruff
31
+ run: |
32
+ pip install ruff
33
+ ruff check --output-format=github .
34
+
35
+ - name: Upload coverage to Codecov
36
+ uses: codecov/codecov-action@v3
37
+ with:
38
+ file: ./coverage.xml
39
+ flags: unittests
@@ -0,0 +1,35 @@
1
+ name: mypy
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python-version: ["3.11"]
11
+
12
+ steps:
13
+ - name: Cancel Previous Runs
14
+ uses: styfle/cancel-workflow-action@0.12.1
15
+ with:
16
+ access_token: ${{ github.token }}
17
+
18
+ - name: Checkout
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+
30
+ - name: mypy
31
+ run: |
32
+ pip install mypy
33
+ pip install '.[tests]'
34
+ mkdir .mypy_cache
35
+ mypy --install-types --non-interactive meteor/ test/
@@ -0,0 +1,55 @@
1
+ name: tests
2
+
3
+ on:
4
+ pull_request:
5
+ schedule:
6
+ - cron: "0 12 * * 1" # monday at noon GMT
7
+
8
+ jobs:
9
+ build:
10
+ name: ${{ matrix.platform }}
11
+ runs-on: ${{ matrix.platform }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version:
16
+ - '3.11'
17
+ platform:
18
+ - ubuntu-latest
19
+ - macos-latest
20
+ - windows-latest
21
+
22
+ steps:
23
+ - name: Cancel Previous Runs
24
+ uses: styfle/cancel-workflow-action@0.12.1
25
+ with:
26
+ access_token: ${{ github.token }}
27
+
28
+ - name: Checkout
29
+ uses: actions/checkout@v4
30
+ with:
31
+ lfs: true
32
+
33
+ - name: Set up Python ${{ matrix.python-version }}
34
+ uses: actions/setup-python@v5
35
+ with:
36
+ python-version: ${{ matrix.python-version }}
37
+
38
+ - name: Install dependencies
39
+ run: |
40
+ python -m pip install --upgrade pip
41
+
42
+ - name: Test with pytest
43
+ run: |
44
+ pip install '.[tests]'
45
+ pytest test/
46
+
47
+ - name: Upload coverage to Codecov
48
+ uses: codecov/codecov-action@v4
49
+ env:
50
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
51
+ with:
52
+ file: ./coverage.xml
53
+ flags: unittests
54
+ name: codecov-umbrella
55
+ fail_ci_if_error: true
@@ -0,0 +1,171 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # large crystallography files -- some of which are used in tests
7
+ # large files are accessed via github LFS
8
+ *.ccp4
9
+ *.map
10
+ *.mtz
11
+ *.pdb
12
+ *.cif
13
+
14
+ # C extensions
15
+ *.so
16
+
17
+ # Distribution / packaging
18
+ _version.py
19
+ .Python
20
+ build/
21
+ develop-eggs/
22
+ dist/
23
+ downloads/
24
+ eggs/
25
+ .eggs/
26
+ lib/
27
+ lib64/
28
+ parts/
29
+ sdist/
30
+ var/
31
+ wheels/
32
+ share/python-wheels/
33
+ *.egg-info/
34
+ .installed.cfg
35
+ *.egg
36
+ MANIFEST
37
+
38
+ # PyInstaller
39
+ # Usually these files are written by a python script from a template
40
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
41
+ *.manifest
42
+ *.spec
43
+
44
+ # Installer logs
45
+ pip-log.txt
46
+ pip-delete-this-directory.txt
47
+
48
+ # Unit test / coverage reports
49
+ htmlcov/
50
+ .tox/
51
+ .nox/
52
+ .coverage
53
+ .coverage.*
54
+ .cache
55
+ nosetests.xml
56
+ coverage.xml
57
+ *.cover
58
+ *.py,cover
59
+ .hypothesis/
60
+ .pytest_cache/
61
+ cover/
62
+
63
+ # Translations
64
+ *.mo
65
+ *.pot
66
+
67
+ # Django stuff:
68
+ *.log
69
+ local_settings.py
70
+ db.sqlite3
71
+ db.sqlite3-journal
72
+
73
+ # Flask stuff:
74
+ instance/
75
+ .webassets-cache
76
+
77
+ # Scrapy stuff:
78
+ .scrapy
79
+
80
+ # Sphinx documentation
81
+ docs/_build/
82
+
83
+ # PyBuilder
84
+ .pybuilder/
85
+ target/
86
+
87
+ # Jupyter Notebook
88
+ .ipynb_checkpoints
89
+
90
+ # IPython
91
+ profile_default/
92
+ ipython_config.py
93
+
94
+ # pyenv
95
+ # For a library or package, you might want to ignore these files since the code is
96
+ # intended to run in multiple environments; otherwise, check them in:
97
+ # .python-version
98
+
99
+ # pipenv
100
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
101
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
102
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
103
+ # install all needed dependencies.
104
+ #Pipfile.lock
105
+
106
+ # poetry
107
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
109
+ # commonly ignored for libraries.
110
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111
+ #poetry.lock
112
+
113
+ # pdm
114
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
115
+ #pdm.lock
116
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
117
+ # in version control.
118
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
119
+ .pdm.toml
120
+ .pdm-python
121
+ .pdm-build/
122
+
123
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
124
+ __pypackages__/
125
+
126
+ # Celery stuff
127
+ celerybeat-schedule
128
+ celerybeat.pid
129
+
130
+ # SageMath parsed files
131
+ *.sage.py
132
+
133
+ # Environments
134
+ .env
135
+ .venv
136
+ env/
137
+ venv/
138
+ ENV/
139
+ env.bak/
140
+ venv.bak/
141
+
142
+ # Spyder project settings
143
+ .spyderproject
144
+ .spyproject
145
+
146
+ # Rope project settings
147
+ .ropeproject
148
+
149
+ # mkdocs documentation
150
+ /site
151
+
152
+ # mypy
153
+ .mypy_cache/
154
+ .dmypy.json
155
+ dmypy.json
156
+
157
+ # Pyre type checker
158
+ .pyre/
159
+
160
+ # pytype static type analyzer
161
+ .pytype/
162
+
163
+ # Cython debug symbols
164
+ cython_debug/
165
+
166
+ # PyCharm
167
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
168
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
169
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
170
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
171
+ #.idea/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Alisia Fadini and T. J. Lane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.1
2
+ Name: meteor-maps
3
+ Version: 0.2.2
4
+ Summary: denoise crystallographic difference maps
5
+ Author-email: Alisia Fadini <af840@cam.ac.uk>, Thomas Lane <thomas.lane@desy.de>
6
+ Requires-Python: >=3.11
7
+ License-File: LICENSE
8
+ Requires-Dist: numpy>=1.26
9
+ Requires-Dist: scipy>=1.14.0
10
+ Requires-Dist: gemmi>=0.6.6
11
+ Requires-Dist: scikit-image>=0.24.0
12
+ Requires-Dist: reciprocalspaceship>=1.0.2
13
+ Requires-Dist: structlog>=24.4.0
14
+ Provides-Extra: tests
15
+ Requires-Dist: pytest; extra == "tests"
16
+ Requires-Dist: pytest-cov; extra == "tests"
17
+ Requires-Dist: pytest-xdist; extra == "tests"
@@ -0,0 +1,58 @@
1
+ # METEOR
2
+ Map Enhancement Tools for Ephemeral Occupancy Refinement
3
+ ----------
4
+
5
+ [![Pytest](https://github.com/alisiafadini/meteor/actions/workflows/tests.yml/badge.svg)](https://github.com/your_username/your_repo/actions/workflows/tests.yml)
6
+ [![Mypy](https://github.com/alisiafadini/meteor/actions/workflows/mypy.yml/badge.svg)](https://github.com/your_username/your_repo/actions/workflows/mypy.yml)
7
+ [![Ruff](https://github.com/alisiafadini/meteor/actions/workflows/lint.yml/badge.svg)](https://github.com/your_username/your_repo/actions/workflows/lint.yml)
8
+ [![codecov](https://codecov.io/gh/alisiafadini/meteor/graph/badge.svg?token=NCYMP06MNS)](https://codecov.io/gh/alisiafadini/meteor)
9
+
10
+ `meteor` is a tool for computing crystallographic difference maps.
11
+
12
+ `meteor` specializes the robust identification of weak signals arising from minor but scientifically interesting populations, such as bound ligands or changes that occur in time-resolved experiments. That said, if you need an difference map, `meteor` can do it!
13
+
14
+
15
+ ## quickstart
16
+
17
+ ❗ `meteor` is currently **in beta**. We re-wrote everything recently, moving from a research code to something that can be robustly used as a tool. If you are willing to put up with a few sharp edges, it would be great if you give it a spin and then send us feedback: on how easy/not it was to use and what kinds of scientific results you obtain.
18
+
19
+ Finally: a word of caution. Expect changes in the coming weeks as we stress test the code. You might want to consider this before publishing any results with `meteor` until we exit `beta`.
20
+
21
+ In that vein, for now please install the current master branch as per the following instructions. We'll have a sane
22
+ first stable version soon, which will be deployed straight to `PyPI` and `conda-forge`. Stay posted.
23
+
24
+ First, we recommend you make a new environment. For conda users,
25
+ ```
26
+ conda create --name meteor python==3.11 --yes
27
+ conda activate meteor
28
+ ```
29
+
30
+ Then install from github,
31
+ ```
32
+ pip install git+https://github.com/alisiafadini/meteor
33
+ ```
34
+
35
+ Once installed, you will have two command-line scripts. Ask for more info using `-h`:
36
+ ```
37
+ meteor.diffmap -h
38
+ meteor.phaseboost -h
39
+ ```
40
+ which compute denoised difference maps using the constant-phase approximation _vs._ iterative phase retrieval, respectively.
41
+
42
+
43
+ ## philosophy: better science through automation
44
+
45
+ `meteor` aims to:
46
+
47
+ 1. maximize signal to noise
48
+ 2. be objective and reproducible (minimize user choice & bias)
49
+ 3. be easy to use
50
+
51
+ Aim 1 is met using structure factor amplitude weighting (e.g. k-weighting, existing art) and TV denoising (new in the context of crystallography). Aims 2 and 3 are met through automatically setting parameters using negentropy maximization. For all the details, see our paper (coming soon to a preprint server near you).
52
+
53
+
54
+ ## isomorphous data, please
55
+
56
+ METEOR is only for isomorphous difference maps, meaning the lattices/symmetries of the `native` and `derivative` datasets are comparable. If you need to compare non-isomorphous lattices, check out [matchmaps](https://github.com/rs-station/matchmaps).
57
+
58
+
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__", "version"]
2
+
3
+ from ._version import __version__, version
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '0.2.2'
16
+ __version_tuple__ = version_tuple = (0, 2, 2)
@@ -0,0 +1,183 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+
5
+ import numpy as np
6
+ import reciprocalspaceship as rs
7
+
8
+ from .rsmap import Map, _assert_is_map
9
+ from .settings import DEFAULT_KPARAMS_TO_SCAN, MAP_SAMPLING
10
+ from .utils import filter_common_indices
11
+ from .validate import ScalarMaximizer, negentropy
12
+
13
+
14
+ def set_common_crystallographic_metadata(map1: Map, map2: Map, *, output: Map) -> None:
15
+ if hasattr(map1, "cell"):
16
+ if hasattr(map2, "cell") and (map1.cell != map2.cell):
17
+ msg = f"`map1.cell` {map1.cell} != `map2.cell` {map2.cell}"
18
+ raise AttributeError(msg)
19
+ output.cell = map1.cell
20
+
21
+ if hasattr(map1, "spacegroup"):
22
+ if hasattr(map2, "spacegroup") and (map1.spacegroup != map2.spacegroup):
23
+ msg = f"`map1.spacegroup` {map1.spacegroup} != "
24
+ msg += f"`map2.spacegroup` {map2.spacegroup}"
25
+ raise AttributeError(msg)
26
+ output.spacegroup = map1.spacegroup
27
+
28
+
29
+ def compute_difference_map(derivative: Map, native: Map) -> Map:
30
+ """
31
+ Computes amplitude and phase differences between native and derivative structure factor sets.
32
+
33
+ It converts the amplitude and phase pairs from both the native and derivative structure factor
34
+ sets into complex numbers, computes the difference, and then converts the result back
35
+ into amplitudes and phases.
36
+
37
+ If uncertainty columns are provided for both native and derivative data, it also propagates the
38
+ uncertainty of the difference in amplitudes.
39
+
40
+ Parameters
41
+ ----------
42
+ derivative: Map
43
+ the derivative amplitudes, phases, uncertainties
44
+ native: Map
45
+ the native amplitudes, phases, uncertainties
46
+
47
+ Returns
48
+ -------
49
+ diffmap: Map
50
+ map corresponding to the complex difference (derivative - native)
51
+ """
52
+ _assert_is_map(derivative, require_uncertainties=False)
53
+ _assert_is_map(native, require_uncertainties=False)
54
+
55
+ derivative, native = filter_common_indices(derivative, native) # type: ignore[assignment]
56
+
57
+ delta_complex = derivative.complex_amplitudes - native.complex_amplitudes
58
+ delta = Map.from_structurefactor(delta_complex, index=native.index)
59
+
60
+ set_common_crystallographic_metadata(derivative, native, output=delta)
61
+
62
+ if derivative.has_uncertainties and native.has_uncertainties:
63
+ prop_uncertainties = np.sqrt(derivative.uncertainties**2 + native.uncertainties**2)
64
+ delta.set_uncertainties(prop_uncertainties)
65
+
66
+ return delta
67
+
68
+
69
+ def compute_kweights(difference_map: Map, *, k_parameter: float) -> rs.DataSeries:
70
+ """
71
+ Compute weights for each structure factor based on DeltaF and its uncertainty.
72
+
73
+ Parameters
74
+ ----------
75
+ difference_map: Map
76
+ A map of structure factor differences (DeltaF).
77
+ k_parameter: float
78
+ A scaling factor applied to the squared `df` values in the weight calculation.
79
+
80
+ Returns
81
+ -------
82
+ weights: rs.DataSeries
83
+ A series of computed weights, where higher uncertainties and larger differences lead to
84
+ lower weights.
85
+ """
86
+ _assert_is_map(difference_map, require_uncertainties=True)
87
+
88
+ inverse_weights = (
89
+ 1
90
+ + (difference_map.uncertainties**2 / (difference_map.uncertainties**2).mean())
91
+ + k_parameter * (difference_map.amplitudes**2 / (difference_map.amplitudes**2).mean())
92
+ )
93
+ return 1.0 / inverse_weights
94
+
95
+
96
+ def compute_kweighted_difference_map(derivative: Map, native: Map, *, k_parameter: float) -> Map:
97
+ """
98
+ Compute k-weighted derivative - native structure factor map.
99
+
100
+ This function first computes the standard difference map using `compute_difference_map`.
101
+ Then, it applies k-weighting to the amplitude differences based on the provided `k_parameter`.
102
+
103
+ Assumes amplitudes have already been scaled prior to invoking this function.
104
+
105
+ Parameters
106
+ ----------
107
+ derivative: Map
108
+ the derivative amplitudes, phases, uncertainties
109
+ native: Map
110
+ the native amplitudes, phases, uncertainties
111
+
112
+ Returns
113
+ -------
114
+ diffmap: Map
115
+ the k-weighted difference map
116
+ """
117
+ # require uncertainties at the beginning
118
+ _assert_is_map(derivative, require_uncertainties=True)
119
+ _assert_is_map(native, require_uncertainties=True)
120
+
121
+ difference_map = compute_difference_map(derivative, native)
122
+ weights = compute_kweights(difference_map, k_parameter=k_parameter)
123
+
124
+ difference_map.amplitudes *= weights
125
+ difference_map.uncertainties *= weights
126
+
127
+ return difference_map
128
+
129
+
130
+ def max_negentropy_kweighted_difference_map(
131
+ derivative: Map,
132
+ native: Map,
133
+ *,
134
+ k_parameter_values_to_scan: np.ndarray | Sequence[float] = DEFAULT_KPARAMS_TO_SCAN,
135
+ ) -> rs.DataSet:
136
+ """
137
+ Compute k-weighted differences between native and derivative amplitudes and phases.
138
+
139
+ Determines an "optimal" k_parameter, between 0.0 and 1.0, that maximizes the resulting
140
+ difference map negentropy. Assumes that scaling has already been applied to the amplitudes
141
+ before calling this function.
142
+
143
+ Parameters
144
+ ----------
145
+ derivative: Map
146
+ the derivative amplitudes, phases, uncertainties
147
+ native: Map
148
+ the native amplitudes, phases, uncertainties
149
+ k_parameter_values_to_scan : np.ndarray | Sequence[float]
150
+ The values to scan to optimize the k-weighting parameter, by default is 0.00, 0.01 ... 1.00
151
+
152
+ Returns
153
+ -------
154
+ kweighted_dataset: rs.DataSet
155
+ dataset with added columns
156
+
157
+ opt_k_parameter: float
158
+ optimized k-weighting parameter
159
+ """
160
+ _assert_is_map(derivative, require_uncertainties=True)
161
+ _assert_is_map(native, require_uncertainties=True)
162
+
163
+ def negentropy_objective(k_parameter: float) -> float:
164
+ kweighted_dataset = compute_kweighted_difference_map(
165
+ derivative,
166
+ native,
167
+ k_parameter=k_parameter,
168
+ )
169
+ k_weighted_map = kweighted_dataset.to_ccp4_map(map_sampling=MAP_SAMPLING)
170
+ k_weighted_map_array = np.array(k_weighted_map.grid)
171
+ return negentropy(k_weighted_map_array)
172
+
173
+ maximizer = ScalarMaximizer(objective=negentropy_objective)
174
+ maximizer.optimize_over_explicit_values(arguments_to_scan=k_parameter_values_to_scan)
175
+ opt_k_parameter = float(maximizer.argument_optimum)
176
+
177
+ kweighted_dataset = compute_kweighted_difference_map(
178
+ derivative,
179
+ native,
180
+ k_parameter=opt_k_parameter,
181
+ )
182
+
183
+ return kweighted_dataset, opt_k_parameter