finalcif 173__tar.gz → 175__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.
- {finalcif-173 → finalcif-175}/.gitignore +1 -0
- {finalcif-173 → finalcif-175}/.ruff.toml +3 -1
- {finalcif-173 → finalcif-175}/.run/compile_ui_files.run.xml +1 -1
- {finalcif-173 → finalcif-175}/PKG-INFO +6 -6
- finalcif-175/finalcif/__init__.py +3 -0
- {finalcif-173 → finalcif-175}/finalcif/appwindow.py +103 -8
- {finalcif-173 → finalcif-175}/finalcif/cif/checkcif/checkcif.py +7 -5
- {finalcif-173 → finalcif-175}/finalcif/cif/hkl.py +73 -9
- {finalcif-173 → finalcif-175}/finalcif/datafiles/bruker_data.py +76 -21
- {finalcif-173 → finalcif-175}/finalcif/datafiles/sadabs.py +138 -5
- {finalcif-173 → finalcif-175}/finalcif/gui/finalcif_gui_ui.py +63 -54
- {finalcif-173 → finalcif-175}/finalcif/gui/finalcif_gui_ui.ui +117 -105
- {finalcif-173 → finalcif-175}/finalcif/gui/validators.py +5 -1
- {finalcif-173 → finalcif-175}/finalcif/tools/squeeze.py +76 -1
- {finalcif-173 → finalcif-175}/finalcif/tools/sumformula.py +27 -3
- {finalcif-173 → finalcif-175}/finalcif/tools/z_from_packing.py +701 -281
- {finalcif-173 → finalcif-175}/pyproject.toml +4 -4
- {finalcif-173 → finalcif-175}/uv.lock +215 -135
- finalcif-173/finalcif/__init__.py +0 -3
- {finalcif-173 → finalcif-175}/.gitattributes +0 -0
- {finalcif-173 → finalcif-175}/.githooks/pre-push +0 -0
- {finalcif-173 → finalcif-175}/.python-version +0 -0
- {finalcif-173 → finalcif-175}/.run/Make Documentation MAC.run.xml +0 -0
- {finalcif-173 → finalcif-175}/.run/make documentation Windows.run.xml +0 -0
- {finalcif-173 → finalcif-175}/AGENTS.md +0 -0
- {finalcif-173 → finalcif-175}/CODE_OF_CONDUCT.md +0 -0
- {finalcif-173 → finalcif-175}/CONTRIBUTING.md +0 -0
- {finalcif-173 → finalcif-175}/Finalcif_installer_win.spec +0 -0
- {finalcif-173 → finalcif-175}/Finalcif_linux_onefile.spec +0 -0
- {finalcif-173 → finalcif-175}/Finalcif_mac.spec +0 -0
- {finalcif-173 → finalcif-175}/LICENSE +0 -0
- {finalcif-173 → finalcif-175}/README.md +0 -0
- {finalcif-173 → finalcif-175}/finalcif/app_path.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/all_cif_dicts.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/atoms.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/checkcif/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cif_dict_foo.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cif_file_io.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cif_keyword_mapping.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cif_order.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/deposit.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/deposit_check.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/deposition_list.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/doi.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/upload.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/cod/website_parser.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/core_dict.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/modulation_dict.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/powder_dict.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/reference.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/restraints_dict.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/text.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/twin_dict.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/cif/vrf_entry.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/ciforder/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/ciforder/order.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/ciforder/order_ui.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/ciforder/order_ui.ui +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/bruker_frame.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/ccdc_mail.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/data.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/p4p_reader.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/saint.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/shelx_lst.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/datafiles/utils.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/equip_property/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/equip_property/author_loop_templates.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/equip_property/equipment.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/equip_property/properties.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/equip_property/tools.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/finalcif_start.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/block_combobox.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/checkcif_browser.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/cif_table_model.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/cif_table_view.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/combobox.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/custom_classes.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/dialogs.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/edit_button.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/equipmenttable.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/file_editor.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/import_selector.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/import_selector_ui.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/import_selector_ui.ui +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/loop_creator.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/loop_creator_ui.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/loop_creator_ui.ui +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/loops.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/mainstackwidget.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/mixins.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/new_key_dialog.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/new_key_dialog_ui.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/new_key_dialog_ui.ui +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/plaintextedit.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/playground.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/propertytable.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/shelx_navigation.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/single_line_textedit.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/spell_check_edit.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/squeeze_dialog.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/syntax_highlighter.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/text_templates_ui.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/text_templates_ui.ui +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/text_value_editor.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/timers.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/vrf_classes.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/gui/vzs_viewer.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/finalcif.png +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/finalcif2.ico +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/finalcif2.png +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/multitable.ico +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/multitable.png +0 -0
- {finalcif-173 → finalcif-175}/finalcif/icon/multitable.xcf +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/archive_report.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/gui/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/gui/mainwindow.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/gui/mainwindow.ui +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/mtools.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/references.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/report_text.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/symm.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/tables.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/report/templated_report.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/machines/D8_VENTURE.cif +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/mathml2omml.xsl +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/miew/LICENSES +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/miew/Miew.css +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/miew/Miew.min.js +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/miew/lodash.min.js +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/miew/three.js +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/report.tex +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/report.tmpl +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/report2.tex +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/report_default.docx +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/report_fastmolwidget.tmpl +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/template1.docx +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/template_for_multitable.docx +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/template_text.docx +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/template_without_text.docx +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/templates.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/FakePlasTeX/FakeContext.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/FakePlasTeX/FakeTokenizer.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/FakePlasTeX/README.md +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/FakePlasTeX/TokenizerPassThru.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/LICENSE +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/u2l.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/unicode-math-table.tex +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/unicode2latex/unicode-math-xetex.sty +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/xsl/convert.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/xsl/xmltohtml.xsl +0 -0
- {finalcif-173 → finalcif-175}/finalcif/template/xsl/xmltolatex.xsl +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/__init__.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/chemparse.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/download.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/dsrmath.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/formal_charge.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/misc.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/mol_file_writer.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/options.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/platon.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/pupdate.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/selfupdate.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/settings.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/shred.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/space_groups.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/spgr_format.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif/tools/statusbar.py +0 -0
- {finalcif-173 → finalcif-175}/finalcif.exe +0 -0
- {finalcif-173 → finalcif-175}/platon/README.txt +0 -0
- {finalcif-173 → finalcif-175}/platon/compile_platon.bat +0 -0
- {finalcif-173 → finalcif-175}/platon/platon_special.exe +0 -0
- {finalcif-173 → finalcif-175}/platon/platon_special.f +0 -0
- {finalcif-173 → finalcif-175}/pylock.toml +0 -0
- {finalcif-173 → finalcif-175}/run_and_capture.py +0 -0
- {finalcif-173 → finalcif-175}/run_finalcif +0 -0
- {finalcif-173 → finalcif-175}/run_finalcif.bat +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_checkcif.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_details.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_loops.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_loops2.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_main.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_report.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/finalcif_responses.png +0 -0
- {finalcif-173 → finalcif-175}/screenshots/loops_column_buttons.png +0 -0
- {finalcif-173 → finalcif-175}/work/compare_z_platon.py +0 -0
- {finalcif-173 → finalcif-175}/work/diagnose_1519506.py +0 -0
- {finalcif-173 → finalcif-175}/work/diagnose_z.py +0 -0
- {finalcif-173 → finalcif-175}/work/investigate_failures.py +0 -0
- {finalcif-173 → finalcif-175}/work/validate_cod.py +0 -0
- {finalcif-173 → finalcif-175}/work/validate_zprime.py +0 -0
|
@@ -51,7 +51,9 @@ select = ["E", "F", "B", "UP", "ANN", "A", "C4", "DTZ", "NPY", "PL", "FURB", "RU
|
|
|
51
51
|
ignore = ["E501", "F401", "E401", "B904", "E402", "C901",
|
|
52
52
|
"PLR0912", "PLR0915", "PLR0913", "ANN201", "ANN201", "ANN204", "ANN001", "ANN002",
|
|
53
53
|
"D102", "D103", "D205", "D212", "PLR2004", "PLW2901", "DTZ005", "DTZ006", "UP015",
|
|
54
|
-
"RUF012",
|
|
54
|
+
"RUF012",
|
|
55
|
+
"DTZ001",
|
|
56
|
+
"PLR0911",
|
|
55
57
|
]
|
|
56
58
|
|
|
57
59
|
# Avoid trying to fix flake8-bugbear (`B`) violations.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
15
15
|
<option name="DEBUG_JUST_MY_CODE" value="true" />
|
|
16
16
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
|
17
|
-
<option name="RUN_TOOL" value="
|
|
17
|
+
<option name="RUN_TOOL" value="false" />
|
|
18
18
|
<option name="SCRIPT_NAME" value="scripts/compile_ui_files.py" />
|
|
19
19
|
<option name="PARAMETERS" value="" />
|
|
20
20
|
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: finalcif
|
|
3
|
-
Version:
|
|
3
|
+
Version: 175
|
|
4
4
|
Summary: CIF file editor
|
|
5
5
|
Project-URL: Homepage, https://dkratzert.de/finalcif.html
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/dkratzert/FinalCif/issues
|
|
@@ -15,14 +15,14 @@ Requires-Python: >=3.12
|
|
|
15
15
|
Requires-Dist: certifi==2026.6.17
|
|
16
16
|
Requires-Dist: chardet
|
|
17
17
|
Requires-Dist: charset-normalizer
|
|
18
|
-
Requires-Dist: docxcompose
|
|
18
|
+
Requires-Dist: docxcompose>=2.2.0
|
|
19
19
|
Requires-Dist: docxtpl[subdoc]>=0.20.2
|
|
20
|
-
Requires-Dist: fastmolwidget>=1.
|
|
20
|
+
Requires-Dist: fastmolwidget>=1.2.0
|
|
21
21
|
Requires-Dist: gemmi>=0.7.5
|
|
22
22
|
Requires-Dist: html2text
|
|
23
23
|
Requires-Dist: lxml>=6.1.1
|
|
24
24
|
Requires-Dist: numpy==2.4.6
|
|
25
|
-
Requires-Dist: packaging==26.
|
|
25
|
+
Requires-Dist: packaging==26.3
|
|
26
26
|
Requires-Dist: pefile
|
|
27
27
|
Requires-Dist: pybind11>=3
|
|
28
28
|
Requires-Dist: pyenchant
|
|
@@ -31,7 +31,7 @@ Requires-Dist: python-docx
|
|
|
31
31
|
Requires-Dist: qtawesome
|
|
32
32
|
Requires-Dist: qtpy
|
|
33
33
|
Requires-Dist: requests
|
|
34
|
-
Requires-Dist: shelxfile
|
|
34
|
+
Requires-Dist: shelxfile>=24
|
|
35
35
|
Requires-Dist: urllib3==2.7.0
|
|
36
36
|
Requires-Dist: wheel==0.47.0
|
|
37
37
|
Description-Content-Type: text/markdown
|
|
@@ -26,6 +26,8 @@ if TYPE_CHECKING:
|
|
|
26
26
|
from finalcif.cif.checkcif.checkcif import MyHTMLParser
|
|
27
27
|
|
|
28
28
|
from fastmolwidget import Atomtuple
|
|
29
|
+
from fastmolwidget.density_controls import ResidualDensityControls
|
|
30
|
+
from fastmolwidget.hkl_io import find_reflection_file, has_reflections
|
|
29
31
|
from fastmolwidget.part_combo import PartFilterWidget
|
|
30
32
|
|
|
31
33
|
from finalcif.gui.shelx_navigation import find_shelx_line_for_atom, scroll_and_highlight_shelx_atom
|
|
@@ -86,10 +88,12 @@ from finalcif.tools.platon import PlatonRunner
|
|
|
86
88
|
from finalcif.tools.settings import FinalCifSettings
|
|
87
89
|
from finalcif.tools.shred import ShredCIF
|
|
88
90
|
from finalcif.tools.space_groups import SpaceGroups
|
|
91
|
+
from finalcif.tools.squeeze import append_unknown_solvent, has_unassigned_solvent
|
|
89
92
|
from finalcif.tools.z_from_packing import count_z_and_zprime, ZResult
|
|
90
93
|
from finalcif.tools.spgr_format import spgrps
|
|
91
94
|
from finalcif.tools.statusbar import StatusBar
|
|
92
|
-
from finalcif.tools.sumformula import formula_str_to_dict, formula_to_html, sum_formula_to_html
|
|
95
|
+
from finalcif.tools.sumformula import (formula_str_to_dict, formula_to_html, sum_formula_to_html,
|
|
96
|
+
with_solvent_marker)
|
|
93
97
|
|
|
94
98
|
DEBUG = False
|
|
95
99
|
app = QApplication.instance()
|
|
@@ -194,6 +198,14 @@ class AppWindow(QMainWindow):
|
|
|
194
198
|
self.parts_combo = PartFilterWidget(parent=self.ui.molGroupBox, label=' ', min_combo_width=90)
|
|
195
199
|
self.parts_combo.setObjectName('partsFilterWidget')
|
|
196
200
|
self.ui.horizontalLayout_7.insertWidget(3, self.parts_combo)
|
|
201
|
+
# Residual density button and level spin box, next to the parts selector.
|
|
202
|
+
# The file dialog is switched off: without reflection data the structure
|
|
203
|
+
# is simply shown without density.
|
|
204
|
+
self.density_controls = ResidualDensityControls(render_widget=self.ui.render_widget,
|
|
205
|
+
parent=self.ui.molGroupBox,
|
|
206
|
+
allow_reflection_dialog=False)
|
|
207
|
+
self.density_controls.setObjectName('residualDensityControls')
|
|
208
|
+
self.ui.horizontalLayout_7.insertWidget(4, self.density_controls)
|
|
197
209
|
# Inject the Author Editor tab (defined in the .ui file) into LoopsPage
|
|
198
210
|
self.ui.loops_page.set_author_editor_tab(self.ui.author_editor_widget)
|
|
199
211
|
self.fixfont = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.SystemFont.FixedFont)
|
|
@@ -1072,7 +1084,6 @@ class AppWindow(QMainWindow):
|
|
|
1072
1084
|
source_item = MyTableWidgetItem(s)
|
|
1073
1085
|
source_item.setUneditable()
|
|
1074
1086
|
data_item = MyTableWidgetItem(self.sources[s][1])
|
|
1075
|
-
data_item.setToolTip(str(self.sources[s][1]))
|
|
1076
1087
|
data_item.setUneditable()
|
|
1077
1088
|
table.setItem(rownum, COL_key, source_item)
|
|
1078
1089
|
table.setItem(rownum, COL_source_data, data_item)
|
|
@@ -1306,8 +1317,9 @@ class AppWindow(QMainWindow):
|
|
|
1306
1317
|
self.ui.CheckCifLogPlainTextEdit.appendHtml(
|
|
1307
1318
|
f'<br><b>{n} validation response forms {form_noun} added to the main table.</b>')
|
|
1308
1319
|
else:
|
|
1320
|
+
levels = 'A, B or C' if self.ui.explainCandGAlertsCheckBox.isChecked() else 'A or B'
|
|
1309
1321
|
self.ui.CheckCifLogPlainTextEdit.appendHtml(
|
|
1310
|
-
'<br><b>No level
|
|
1322
|
+
f'<br><b>No level {levels} alerts require a response.</b>')
|
|
1311
1323
|
|
|
1312
1324
|
def _vrf_insert_position(self) -> int:
|
|
1313
1325
|
"""Return the row index just after the last existing ``_vrf_*`` row in the main table.
|
|
@@ -1359,7 +1371,8 @@ class AppWindow(QMainWindow):
|
|
|
1359
1371
|
hkl_upload=(not self.ui.structfactCheckBox.isChecked()), pdf=False,
|
|
1360
1372
|
url=self.options.checkcif_url,
|
|
1361
1373
|
full_iucr=self.ui.fullIucrCheckBox.isChecked(),
|
|
1362
|
-
check_duplicates=self.ui.checkDuplicatesCheckBox.isChecked()
|
|
1374
|
+
check_duplicates=self.ui.checkDuplicatesCheckBox.isChecked(),
|
|
1375
|
+
explain_c=self.ui.explainCAlertsCheckBox.isChecked())
|
|
1363
1376
|
self.ckf.progress.connect(self._ckf_progress)
|
|
1364
1377
|
self.ckf.failed.connect(self._checkcif_failed)
|
|
1365
1378
|
self.ckf.finished.connect(self._checkcif_finished)
|
|
@@ -1507,6 +1520,50 @@ class AppWindow(QMainWindow):
|
|
|
1507
1520
|
self.ui.cif_main_table.setText(key='_chemical_formula_moiety', txt=formula_moiety, column=Column.EDIT)
|
|
1508
1521
|
self.show_moiety_formula_tooltip(moiety if moiety not in ['', '?'] else formula_moiety)
|
|
1509
1522
|
|
|
1523
|
+
def _with_squeezed_solvent(self, formula_moiety: str) -> str:
|
|
1524
|
+
"""Mark a generated moiety formula with ``[+ solvent]`` for squeezed structures.
|
|
1525
|
+
|
|
1526
|
+
The unmodelled solvent of a PLATON SQUEEZE / Olex2-SMTBX structure is
|
|
1527
|
+
missing from the atom model, so the moiety derived from the bond graph
|
|
1528
|
+
is incomplete unless the void content was assigned a formula.
|
|
1529
|
+
"""
|
|
1530
|
+
try:
|
|
1531
|
+
if has_unassigned_solvent(self.cif):
|
|
1532
|
+
return append_unknown_solvent(formula_moiety)
|
|
1533
|
+
except Exception:
|
|
1534
|
+
pass
|
|
1535
|
+
return formula_moiety
|
|
1536
|
+
|
|
1537
|
+
def mark_squeezed_solvent_in_formulas(self) -> None:
|
|
1538
|
+
"""Append ``[+ solvent]`` to the sum and moiety formula of a squeezed structure.
|
|
1539
|
+
|
|
1540
|
+
PLATON marks both ``_chemical_formula_sum`` and
|
|
1541
|
+
``_chemical_formula_moiety`` this way whenever solvent was removed by
|
|
1542
|
+
SQUEEZE or an Olex2/SMTBX mask, because the formulas then describe less
|
|
1543
|
+
than the real content of the unit cell. Nothing is marked when the void
|
|
1544
|
+
content was assigned a formula, or for multi-CIF files.
|
|
1545
|
+
"""
|
|
1546
|
+
try:
|
|
1547
|
+
if self.cif.is_multi_cif or not has_unassigned_solvent(self.cif):
|
|
1548
|
+
return
|
|
1549
|
+
except Exception:
|
|
1550
|
+
return
|
|
1551
|
+
for key in ('_chemical_formula_sum', '_chemical_formula_moiety'):
|
|
1552
|
+
self._append_solvent_marker_to(key)
|
|
1553
|
+
|
|
1554
|
+
def _append_solvent_marker_to(self, key: str) -> None:
|
|
1555
|
+
"""Write *key* back with the unknown-solvent marker unless it is empty.
|
|
1556
|
+
|
|
1557
|
+
``append_unknown_solvent`` normalises a marker that a previous save
|
|
1558
|
+
wrapped across two CIF lines, so the comparison has to be made against
|
|
1559
|
+
the normalised value to avoid writing the row over and over.
|
|
1560
|
+
"""
|
|
1561
|
+
current = (self.ui.cif_main_table.getTextFromKey(key=key, col=Column.EDIT)
|
|
1562
|
+
or self.ui.cif_main_table.getTextFromKey(key=key, col=Column.CIF))
|
|
1563
|
+
marked = append_unknown_solvent(current)
|
|
1564
|
+
if marked and marked != current.strip():
|
|
1565
|
+
self.ui.cif_main_table.setText(key=key, txt=marked, column=Column.EDIT)
|
|
1566
|
+
|
|
1510
1567
|
def append_to_ciflog_without_newline(self, text: str = '') -> None:
|
|
1511
1568
|
self.ui.CheckCifLogPlainTextEdit.moveCursor(QtGui.QTextCursor.MoveOperation.End)
|
|
1512
1569
|
self.ui.CheckCifLogPlainTextEdit.insertPlainText(text)
|
|
@@ -2047,6 +2104,7 @@ class AppWindow(QMainWindow):
|
|
|
2047
2104
|
self.enable_buttons()
|
|
2048
2105
|
self.fill_space_group_lineedit()
|
|
2049
2106
|
self.fill_sum_formula_lineedit()
|
|
2107
|
+
self.mark_squeezed_solvent_in_formulas()
|
|
2050
2108
|
self.ui.CCDCNumLineEdit.setText(self.cif['_database_code_depnum_ccdc_archive'])
|
|
2051
2109
|
self.ui.CheckcifPlaintextEdit.clear()
|
|
2052
2110
|
self.ui.TemplatesStackedWidget.setCurrentIndex(0)
|
|
@@ -2085,11 +2143,12 @@ class AppWindow(QMainWindow):
|
|
|
2085
2143
|
return False
|
|
2086
2144
|
|
|
2087
2145
|
def fill_sum_formula_lineedit(self) -> None:
|
|
2146
|
+
formula = self.cif['_chemical_formula_sum'].strip(" '")
|
|
2088
2147
|
try:
|
|
2089
|
-
|
|
2090
|
-
|
|
2148
|
+
html = sum_formula_to_html(formula_str_to_dict(formula))
|
|
2149
|
+
self.ui.SumFormMainLineEdit.setText(with_solvent_marker(html, formula))
|
|
2091
2150
|
except Exception:
|
|
2092
|
-
self.ui.SumFormMainLineEdit.setText(
|
|
2151
|
+
self.ui.SumFormMainLineEdit.setText(formula)
|
|
2093
2152
|
self.show_moiety_formula_tooltip()
|
|
2094
2153
|
|
|
2095
2154
|
def show_moiety_formula_tooltip(self, formula_moiety: str = '') -> None:
|
|
@@ -2256,6 +2315,7 @@ class AppWindow(QMainWindow):
|
|
|
2256
2315
|
if peak:
|
|
2257
2316
|
self.ui.peakLineEdit.setText("{} / {}".format(peak, self.cif['_refine_diff_density_min']))
|
|
2258
2317
|
self._show_shelx_file()
|
|
2318
|
+
self._update_residual_density_source()
|
|
2259
2319
|
try:
|
|
2260
2320
|
single_shot(self, 0, lambda: self.view_molecule(reset_view=True))
|
|
2261
2321
|
except Exception:
|
|
@@ -2317,6 +2377,29 @@ class AppWindow(QMainWindow):
|
|
|
2317
2377
|
self.ui.render_widget.grow_molecule(atom_tuples,
|
|
2318
2378
|
cell=self.cif.cell[:6])
|
|
2319
2379
|
|
|
2380
|
+
def _update_residual_density_source(self) -> None:
|
|
2381
|
+
"""Tell the viewer which model and reflections the shown atoms come from.
|
|
2382
|
+
|
|
2383
|
+
The block in memory is used, not the file on disk, so that the density
|
|
2384
|
+
follows unsaved edits and the block the user selected in a multi-block
|
|
2385
|
+
CIF. Without any reflection data the density button stays disabled and
|
|
2386
|
+
the structure is simply displayed without density.
|
|
2387
|
+
"""
|
|
2388
|
+
with suppress(Exception):
|
|
2389
|
+
self.ui.render_widget.set_model_source(self.cif.block,
|
|
2390
|
+
reflections=self._reflection_source())
|
|
2391
|
+
self.density_controls.update_density_availability()
|
|
2392
|
+
|
|
2393
|
+
def _reflection_source(self) -> gemmi.cif.Block | Path | None:
|
|
2394
|
+
"""Where the reflections of the current structure are, if anywhere.
|
|
2395
|
+
|
|
2396
|
+
Self-contained SHELXL CIFs carry the whole ``.hkl`` in the block itself;
|
|
2397
|
+
otherwise a sibling ``.hkl``/``.fcf`` of the CIF on disk is used.
|
|
2398
|
+
"""
|
|
2399
|
+
if has_reflections(self.cif.block):
|
|
2400
|
+
return self.cif.block
|
|
2401
|
+
return find_reflection_file(self.cif.fileobj)
|
|
2402
|
+
|
|
2320
2403
|
def _update_z_label(self) -> None:
|
|
2321
2404
|
"""Start a background computation of Z and Z′ and show a placeholder.
|
|
2322
2405
|
|
|
@@ -2372,7 +2455,7 @@ class AppWindow(QMainWindow):
|
|
|
2372
2455
|
return
|
|
2373
2456
|
# Propagate moiety formula to the CIF table if not already filled.
|
|
2374
2457
|
if result.moiety_formula:
|
|
2375
|
-
self.add_moiety_furmula(result.moiety_formula)
|
|
2458
|
+
self.add_moiety_furmula(self._with_squeezed_solvent(result.moiety_formula))
|
|
2376
2459
|
confidence_icon = {'high': '✓', 'medium': '~', 'formula': '≡', 'low': '?'}[result.confidence]
|
|
2377
2460
|
self.ui.zEstimateLabel.setText(f"Z = {result.z} Z′ = {result.z_prime:.3g} {confidence_icon}")
|
|
2378
2461
|
self.ui.zEstimateLabel2.setText(f"Z = {result.z} Z′ = {result.z_prime:.3g} {confidence_icon}")
|
|
@@ -2543,6 +2626,18 @@ class AppWindow(QMainWindow):
|
|
|
2543
2626
|
# print(e, '##', miss_key)
|
|
2544
2627
|
pass
|
|
2545
2628
|
self._apply_source_overrides(bruker_data.overrides)
|
|
2629
|
+
self._add_source_tooltips()
|
|
2630
|
+
|
|
2631
|
+
def _add_source_tooltips(self) -> None:
|
|
2632
|
+
"""
|
|
2633
|
+
Shows the file a value of the data source column came from as a tooltip.
|
|
2634
|
+
"""
|
|
2635
|
+
for key, source in self.sources.items():
|
|
2636
|
+
if not source or not source[1] or not self.ui.cif_main_table.has_key(key):
|
|
2637
|
+
continue
|
|
2638
|
+
widget = self.ui.cif_main_table.widget_from_key(key, Column.DATA)
|
|
2639
|
+
if widget:
|
|
2640
|
+
widget.setToolTip(f'Source: {source[1]}')
|
|
2546
2641
|
|
|
2547
2642
|
def _apply_source_overrides(self, overrides: set[str]) -> None:
|
|
2548
2643
|
"""
|
|
@@ -215,7 +215,8 @@ class CheckCif(QThread):
|
|
|
215
215
|
failed = Signal(str)
|
|
216
216
|
|
|
217
217
|
def __init__(self, parent, cif: CifContainer, outfile: Path, hkl_upload: bool = True,
|
|
218
|
-
pdf: bool = False, url: str = '', full_iucr: bool = False, check_duplicates: bool = True
|
|
218
|
+
pdf: bool = False, url: str = '', full_iucr: bool = False, check_duplicates: bool = True,
|
|
219
|
+
explain_c: bool = False):
|
|
219
220
|
# hkl == False means no hkl upload
|
|
220
221
|
super().__init__(parent=parent)
|
|
221
222
|
self.hkl_upload = hkl_upload
|
|
@@ -225,6 +226,7 @@ class CheckCif(QThread):
|
|
|
225
226
|
self.checkcif_url = url
|
|
226
227
|
self.full_iucr = full_iucr
|
|
227
228
|
self.check_duplicates = check_duplicates
|
|
229
|
+
self.explain_c = explain_c
|
|
228
230
|
|
|
229
231
|
def _html_check(self) -> None:
|
|
230
232
|
if self.hkl_upload:
|
|
@@ -232,12 +234,12 @@ class CheckCif(QThread):
|
|
|
232
234
|
else:
|
|
233
235
|
self.progress.emit('Running Checkcif with no hkl data')
|
|
234
236
|
|
|
235
|
-
def get_vrf(self):
|
|
237
|
+
def get_vrf(self) -> str:
|
|
238
|
+
"""The 'valout' request value defining which alert levels get a response form."""
|
|
236
239
|
if self.pdf:
|
|
237
240
|
return 'vrfno'
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return 'vrfabc'
|
|
241
|
+
# The IUCr server offers no response forms for level G alerts.
|
|
242
|
+
return 'vrfabc' if self.explain_c else 'vrfab'
|
|
241
243
|
|
|
242
244
|
def run(self) -> None:
|
|
243
245
|
"""
|
|
@@ -90,28 +90,42 @@ class HKL:
|
|
|
90
90
|
return Limit(h_max=h_max, h_min=h_min, k_max=k_max, k_min=k_min, l_max=l_max, l_min=l_min)
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
def calculate_rint(hkl_file: str, space_group: str, cell: Sequence[float] | None = None,
|
|
94
|
-
resolution: tuple[float, float] | None = None
|
|
93
|
+
def calculate_rint(hkl_file: str, space_group: str, *, cell: Sequence[float] | None = None,
|
|
94
|
+
resolution: tuple[float, float] | None = None, hklf: int = 4,
|
|
95
|
+
twst: int | None = None) -> float | None:
|
|
95
96
|
"""
|
|
96
|
-
R(int) of an unmerged SHELX
|
|
97
|
+
R(int) of an unmerged SHELX hkl file, calculated the way SHELXL and Olex2 do it:
|
|
97
98
|
|
|
98
99
|
R(int) = sum|Fo^2 - Fo^2(mean)| / sum[Fo^2]
|
|
99
100
|
|
|
100
101
|
Systematically absent reflections are rejected and Friedel opposites are only merged in
|
|
101
102
|
centrosymmetric space groups. Fo^2(mean) is the mean weighted with 1/sigma^2.
|
|
102
103
|
|
|
104
|
+
For HKLF 5 data only the singly indexed reflections of the reference domain are used,
|
|
105
|
+
because composite (overlapped) reflections have no symmetry equivalents to compare with.
|
|
106
|
+
|
|
103
107
|
SADABS writes no overall R(int) into its listing file, only the wR2(int) of the parameter
|
|
104
|
-
refinement and the R(int) values of the individual runs.
|
|
108
|
+
refinement and the R(int) values of the individual runs. The R(int) of a TWINABS listing
|
|
109
|
+
file describes a data treatment that a HKLF 5 refinement does not use.
|
|
105
110
|
|
|
106
111
|
Args:
|
|
107
|
-
hkl_file: Content of a SHELX
|
|
112
|
+
hkl_file: Content of a SHELX hkl file.
|
|
108
113
|
space_group: Space group name, e.g. 'P n a 21'.
|
|
109
114
|
cell: Unit cell parameters, needed to apply a resolution limit.
|
|
110
115
|
resolution: Lowest and highest resolution in Angstrom (a SHELX SHEL instruction).
|
|
116
|
+
hklf: The SHELX HKLF file type. Data with negative batch numbers are always treated as
|
|
117
|
+
HKLF 5 data.
|
|
118
|
+
twst: The domain of a SHELX TWST instruction, only used for HKLF 5 data.
|
|
111
119
|
"""
|
|
112
|
-
miller, intensities, sigmas = _reflections_from_shelx_hkl(hkl_file)
|
|
120
|
+
miller, intensities, sigmas, batches = _reflections_from_shelx_hkl(hkl_file)
|
|
113
121
|
if not len(miller) or not space_group:
|
|
114
122
|
return None
|
|
123
|
+
if hklf >= 5 or bool((batches < 0).any()):
|
|
124
|
+
# Only an HKLF 5 file has negative batch numbers:
|
|
125
|
+
singles = _singles_of_reference_domain(batches, twst)
|
|
126
|
+
miller, intensities, sigmas = miller[singles], intensities[singles], sigmas[singles]
|
|
127
|
+
if not len(miller):
|
|
128
|
+
return None
|
|
115
129
|
try:
|
|
116
130
|
operations = gemmi.SpaceGroup(space_group).operations()
|
|
117
131
|
except (RuntimeError, ValueError):
|
|
@@ -128,6 +142,45 @@ def calculate_rint(hkl_file: str, space_group: str, cell: Sequence[float] | None
|
|
|
128
142
|
return _rint_of_groups(groups, counts, intensities, sigmas)
|
|
129
143
|
|
|
130
144
|
|
|
145
|
+
def reference_domain(hkl_file: str, twst: int | None = None) -> int | None:
|
|
146
|
+
"""
|
|
147
|
+
The twin domain whose singly indexed reflections are used for the R(int) of HKLF 5 data.
|
|
148
|
+
"""
|
|
149
|
+
*_, batches = _reflections_from_shelx_hkl(hkl_file)
|
|
150
|
+
if not len(batches):
|
|
151
|
+
return None
|
|
152
|
+
return _largest_domain(batches[_singles(batches)]) or None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _singles_of_reference_domain(batches: np.ndarray, twst: int | None) -> np.ndarray:
|
|
156
|
+
"""
|
|
157
|
+
The singly indexed reflections of the reference domain of an HKLF 5 file.
|
|
158
|
+
|
|
159
|
+
The reference domain is the domain of a SHELX TWST instruction or, like in Olex2, the
|
|
160
|
+
domain with the most singly indexed reflections.
|
|
161
|
+
"""
|
|
162
|
+
singles = _singles(batches)
|
|
163
|
+
domain = twst if twst and twst > 0 else _largest_domain(batches[singles])
|
|
164
|
+
return singles & (batches == domain)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _singles(batches: np.ndarray) -> np.ndarray:
|
|
168
|
+
"""
|
|
169
|
+
In an HKLF 5 file all members of a composite reflection except the last one have a negative
|
|
170
|
+
batch number, thus a reflection is a single if its own batch number and that of its
|
|
171
|
+
predecessor are positive.
|
|
172
|
+
"""
|
|
173
|
+
previous = np.concatenate(([1], batches[:-1]))
|
|
174
|
+
return (batches > 0) & (previous > 0)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _largest_domain(domains: np.ndarray) -> int:
|
|
178
|
+
if not len(domains):
|
|
179
|
+
return 0
|
|
180
|
+
values, counts = np.unique(domains, return_counts=True)
|
|
181
|
+
return int(values[np.argmax(counts)])
|
|
182
|
+
|
|
183
|
+
|
|
131
184
|
def _within_resolution(miller: np.ndarray, cell: Sequence[float] | None,
|
|
132
185
|
resolution: tuple[float, float] | None) -> np.ndarray:
|
|
133
186
|
"""
|
|
@@ -197,8 +250,8 @@ def _encode(miller: np.ndarray) -> np.ndarray:
|
|
|
197
250
|
return (shifted[:, 0] << 42) | (shifted[:, 1] << 21) | shifted[:, 2]
|
|
198
251
|
|
|
199
252
|
|
|
200
|
-
def _reflections_from_shelx_hkl(hkl_file: str) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
201
|
-
miller, intensities, sigmas = [], [], []
|
|
253
|
+
def _reflections_from_shelx_hkl(hkl_file: str) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
254
|
+
miller, intensities, sigmas, batches = [], [], [], []
|
|
202
255
|
for line in hkl_file.splitlines(keepends=False):
|
|
203
256
|
if len(line) < 28:
|
|
204
257
|
continue
|
|
@@ -212,9 +265,20 @@ def _reflections_from_shelx_hkl(hkl_file: str) -> tuple[np.ndarray, np.ndarray,
|
|
|
212
265
|
miller.append(index)
|
|
213
266
|
intensities.append(intensity)
|
|
214
267
|
sigmas.append(sigma)
|
|
268
|
+
batches.append(_batch_number(line))
|
|
215
269
|
return (np.array(miller, dtype=np.int32).reshape(-1, 3),
|
|
216
270
|
np.array(intensities, dtype=np.float64),
|
|
217
|
-
np.array(sigmas, dtype=np.float64)
|
|
271
|
+
np.array(sigmas, dtype=np.float64),
|
|
272
|
+
np.array(batches, dtype=np.int32))
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _batch_number(line: str) -> int:
|
|
276
|
+
"""The batch number of an HKLF 5 reflection, negative for all but the last member of a
|
|
277
|
+
composite reflection."""
|
|
278
|
+
try:
|
|
279
|
+
return int(line[28:32])
|
|
280
|
+
except ValueError:
|
|
281
|
+
return 1
|
|
218
282
|
|
|
219
283
|
|
|
220
284
|
if __name__ == '__main__':
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
# and you think this stuff is worth it, you can buy me a beer in return.
|
|
6
6
|
# Dr. Daniel Kratzert
|
|
7
7
|
# ----------------------------------------------------------------------------
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import re
|
|
9
11
|
from contextlib import suppress
|
|
10
12
|
from pathlib import Path
|
|
@@ -12,11 +14,11 @@ from pathlib import Path
|
|
|
12
14
|
from gemmi import cif as gcif
|
|
13
15
|
|
|
14
16
|
from finalcif.cif.cif_file_io import CifContainer
|
|
15
|
-
from finalcif.cif.hkl import calculate_rint
|
|
17
|
+
from finalcif.cif.hkl import calculate_rint, reference_domain
|
|
16
18
|
from finalcif.datafiles.bruker_frame import BrukerFrameHeader
|
|
17
19
|
from finalcif.datafiles.data import WorkDataMixin
|
|
18
20
|
from finalcif.datafiles.p4p_reader import P4PFile
|
|
19
|
-
from finalcif.datafiles.sadabs import Sadabs
|
|
21
|
+
from finalcif.datafiles.sadabs import Dataset, Sadabs
|
|
20
22
|
from finalcif.datafiles.saint import SaintListFile
|
|
21
23
|
from finalcif.datafiles.shelx_lst import SolutionProgram
|
|
22
24
|
from finalcif.gui.dialogs import show_general_warning
|
|
@@ -231,38 +233,91 @@ class BrukerData(WorkDataMixin):
|
|
|
231
233
|
"""
|
|
232
234
|
Adds the number of measured reflections and R(int) of the data set that belongs to the
|
|
233
235
|
hkl file of this refinement. SHELXL can not determine these values from an HKLF 5 file,
|
|
234
|
-
thus the
|
|
236
|
+
thus the number of reflections of a TWINABS file and a calculated R(int) take precedence
|
|
237
|
+
over the values in the CIF.
|
|
235
238
|
"""
|
|
236
239
|
dataset = sadabs.select_dataset(hkl_basename=self._hkl_basename,
|
|
237
240
|
reflections=self._reflections_in_hkl_file(),
|
|
238
241
|
hklf=self.cif.hklf_number)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
rint = dataset.rint or self._rint_from_hkl_data()
|
|
242
|
-
if rint:
|
|
243
|
-
self.sources['_diffrn_reflns_av_R_equivalents'] = (rint, self._rint_source(dataset, sadabs))
|
|
244
|
-
if dataset.reflections_number:
|
|
242
|
+
self._add_rint(dataset, sadabs)
|
|
243
|
+
if dataset and dataset.reflections_number:
|
|
245
244
|
self.sources['_diffrn_reflns_number'] = (dataset.reflections_number, source_path(sadabs.filename))
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
if sadabs.is_twinabs and dataset.filetype == 5:
|
|
246
|
+
self.overrides.add('_diffrn_reflns_number')
|
|
248
247
|
|
|
249
|
-
def
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
def _add_rint(self, dataset: Dataset | None, sadabs: Sadabs) -> None:
|
|
249
|
+
"""
|
|
250
|
+
The R(int) of an HKLF 5 refinement is calculated from the reflection data, because the
|
|
251
|
+
R(int) of a TWINABS listing file is a residual of the TWINABS twin fraction refinement
|
|
252
|
+
and not the agreement of symmetry equivalent reflections.
|
|
253
|
+
"""
|
|
254
|
+
calculated = self._rint_from_hkl_data()
|
|
255
|
+
listed = dataset.rint if dataset else None
|
|
256
|
+
twinned = self._is_twinned(dataset, sadabs)
|
|
257
|
+
if twinned:
|
|
258
|
+
rint, source = calculated, self._calculated_rint_source(twinned)
|
|
259
|
+
if not rint:
|
|
260
|
+
rint, source = listed, self._listed_rint_source(dataset, sadabs)
|
|
261
|
+
else:
|
|
262
|
+
rint, source = listed, source_path(sadabs.filename)
|
|
263
|
+
if not rint:
|
|
264
|
+
rint, source = calculated, self._calculated_rint_source(twinned)
|
|
265
|
+
if not rint:
|
|
266
|
+
return
|
|
267
|
+
self.sources['_diffrn_reflns_av_R_equivalents'] = (rint, source)
|
|
268
|
+
if twinned:
|
|
269
|
+
self.overrides.add('_diffrn_reflns_av_R_equivalents')
|
|
270
|
+
|
|
271
|
+
def _is_twinned(self, dataset: Dataset | None, sadabs: Sadabs) -> bool:
|
|
272
|
+
"""
|
|
273
|
+
SHELXL can not determine R(int) of HKLF 5 data, thus FinalCif has to do it.
|
|
274
|
+
"""
|
|
275
|
+
return self.cif.hklf_number >= 5 or bool(
|
|
276
|
+
sadabs.is_twinabs and dataset and dataset.filetype == 5)
|
|
277
|
+
|
|
278
|
+
def _calculated_rint_source(self, twinned: bool) -> str:
|
|
279
|
+
if twinned:
|
|
280
|
+
return (f'calculated from {source_path(self.cif.fileobj)} '
|
|
281
|
+
f'(singles of twin component {self._reference_domain()}, '
|
|
282
|
+
f'merged in {self.cif.space_group})')
|
|
252
283
|
return f'calculated from {source_path(self.cif.fileobj)}'
|
|
253
284
|
|
|
285
|
+
def _reference_domain(self) -> int | None:
|
|
286
|
+
return self._twst() or reference_domain(self.cif.hkl_file)
|
|
287
|
+
|
|
288
|
+
def _listed_rint_source(self, dataset: Dataset | None, sadabs: Sadabs) -> str:
|
|
289
|
+
"""
|
|
290
|
+
The R(int) of the TWINABS statistics is only valid for the point group the equivalent
|
|
291
|
+
reflections were defined with.
|
|
292
|
+
"""
|
|
293
|
+
source = source_path(sadabs.filename)
|
|
294
|
+
statistics = dataset.singles_statistics if dataset else None
|
|
295
|
+
if statistics and dataset.equivalents_point_group:
|
|
296
|
+
return (f'{source} (singles of twin component {statistics.component}, '
|
|
297
|
+
f'point group {dataset.equivalents_point_group})')
|
|
298
|
+
return source
|
|
299
|
+
|
|
254
300
|
def _rint_from_hkl_data(self) -> float | None:
|
|
255
301
|
"""
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
302
|
+
R(int) calculated from the reflection data of the CIF.
|
|
303
|
+
|
|
304
|
+
SADABS writes no overall R(int) into its listing file and SHELXL can not determine it
|
|
305
|
+
from an HKLF 5 file, therefore it is calculated here. For HKLF 4 data this only happens
|
|
306
|
+
if the CIF has no R(int) yet, while for HKLF 5 data the calculated value is always
|
|
307
|
+
preferred over the R(int) of a TWINABS listing file, which is a model residual of the
|
|
308
|
+
TWINABS twin fraction refinement instead of a merging R(int).
|
|
259
309
|
"""
|
|
260
|
-
if self.cif.hklf_number
|
|
261
|
-
|
|
262
|
-
if gcif.as_string(self.cif['_diffrn_reflns_av_R_equivalents']).strip(' ?'):
|
|
310
|
+
if self.cif.hklf_number == 4 and gcif.as_string(
|
|
311
|
+
self.cif['_diffrn_reflns_av_R_equivalents']).strip(' ?'):
|
|
263
312
|
return None
|
|
264
313
|
return calculate_rint(self.cif.hkl_file, self.cif.space_group,
|
|
265
|
-
cell=self.cif.cell, resolution=self._resolution_limits()
|
|
314
|
+
cell=self.cif.cell, resolution=self._resolution_limits(),
|
|
315
|
+
hklf=self.cif.hklf_number, twst=self._twst())
|
|
316
|
+
|
|
317
|
+
def _twst(self) -> int | None:
|
|
318
|
+
"""The reference domain of a SHELX TWST instruction."""
|
|
319
|
+
match = re.search(r'^TWST\s+(-?\d+)', self.cif.res_file_data or '', re.MULTILINE)
|
|
320
|
+
return int(match.group(1)) if match else None
|
|
266
321
|
|
|
267
322
|
def _resolution_limits(self) -> tuple[float, float] | None:
|
|
268
323
|
"""The resolution limits of a SHELX SHEL instruction, if there is one."""
|