finalcif 172__tar.gz → 173__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-172 → finalcif-173}/PKG-INFO +1 -1
- finalcif-173/finalcif/__init__.py +3 -0
- {finalcif-172 → finalcif-173}/finalcif/appwindow.py +18 -2
- finalcif-173/finalcif/cif/hkl.py +224 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/bruker_data.py +121 -43
- {finalcif-172 → finalcif-173}/finalcif/datafiles/data.py +3 -0
- finalcif-173/finalcif/datafiles/sadabs.py +412 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/saint.py +52 -53
- {finalcif-172 → finalcif-173}/finalcif/gui/dialogs.py +1 -1
- {finalcif-172 → finalcif-173}/finalcif/template/report_fastmolwidget.tmpl +11 -1
- {finalcif-172 → finalcif-173}/finalcif/tools/selfupdate.py +129 -16
- finalcif-172/finalcif/__init__.py +0 -3
- finalcif-172/finalcif/cif/hkl.py +0 -94
- finalcif-172/finalcif/datafiles/sadabs.py +0 -191
- {finalcif-172 → finalcif-173}/.gitattributes +0 -0
- {finalcif-172 → finalcif-173}/.githooks/pre-push +0 -0
- {finalcif-172 → finalcif-173}/.gitignore +0 -0
- {finalcif-172 → finalcif-173}/.python-version +0 -0
- {finalcif-172 → finalcif-173}/.ruff.toml +0 -0
- {finalcif-172 → finalcif-173}/.run/Make Documentation MAC.run.xml +0 -0
- {finalcif-172 → finalcif-173}/.run/compile_ui_files.run.xml +0 -0
- {finalcif-172 → finalcif-173}/.run/make documentation Windows.run.xml +0 -0
- {finalcif-172 → finalcif-173}/AGENTS.md +0 -0
- {finalcif-172 → finalcif-173}/CODE_OF_CONDUCT.md +0 -0
- {finalcif-172 → finalcif-173}/CONTRIBUTING.md +0 -0
- {finalcif-172 → finalcif-173}/Finalcif_installer_win.spec +0 -0
- {finalcif-172 → finalcif-173}/Finalcif_linux_onefile.spec +0 -0
- {finalcif-172 → finalcif-173}/Finalcif_mac.spec +0 -0
- {finalcif-172 → finalcif-173}/LICENSE +0 -0
- {finalcif-172 → finalcif-173}/README.md +0 -0
- {finalcif-172 → finalcif-173}/finalcif/app_path.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/all_cif_dicts.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/atoms.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/checkcif/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/checkcif/checkcif.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cif_dict_foo.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cif_file_io.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cif_keyword_mapping.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cif_order.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/deposit.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/deposit_check.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/deposition_list.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/doi.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/upload.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/cod/website_parser.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/core_dict.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/modulation_dict.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/powder_dict.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/reference.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/restraints_dict.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/text.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/twin_dict.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/cif/vrf_entry.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/ciforder/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/ciforder/order.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/ciforder/order_ui.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/ciforder/order_ui.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/bruker_frame.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/ccdc_mail.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/p4p_reader.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/shelx_lst.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/datafiles/utils.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/equip_property/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/equip_property/author_loop_templates.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/equip_property/equipment.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/equip_property/properties.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/equip_property/tools.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/finalcif_start.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/block_combobox.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/checkcif_browser.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/cif_table_model.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/cif_table_view.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/combobox.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/custom_classes.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/edit_button.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/equipmenttable.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/file_editor.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/finalcif_gui_ui.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/finalcif_gui_ui.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/import_selector.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/import_selector_ui.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/import_selector_ui.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/loop_creator.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/loop_creator_ui.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/loop_creator_ui.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/loops.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/mainstackwidget.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/mixins.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/new_key_dialog.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/new_key_dialog_ui.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/new_key_dialog_ui.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/plaintextedit.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/playground.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/propertytable.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/shelx_navigation.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/single_line_textedit.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/spell_check_edit.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/squeeze_dialog.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/syntax_highlighter.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/text_templates_ui.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/text_templates_ui.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/text_value_editor.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/timers.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/validators.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/vrf_classes.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/gui/vzs_viewer.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/finalcif.png +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/finalcif2.ico +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/finalcif2.png +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/multitable.ico +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/multitable.png +0 -0
- {finalcif-172 → finalcif-173}/finalcif/icon/multitable.xcf +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/archive_report.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/gui/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/gui/mainwindow.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/gui/mainwindow.ui +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/mtools.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/references.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/report_text.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/symm.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/tables.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/report/templated_report.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/machines/D8_VENTURE.cif +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/mathml2omml.xsl +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/miew/LICENSES +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/miew/Miew.css +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/miew/Miew.min.js +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/miew/lodash.min.js +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/miew/three.js +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/report.tex +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/report.tmpl +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/report2.tex +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/report_default.docx +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/template1.docx +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/template_for_multitable.docx +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/template_text.docx +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/template_without_text.docx +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/templates.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/FakeContext.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/FakeTokenizer.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/README.md +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/TokenizerPassThru.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/LICENSE +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/u2l.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/unicode-math-table.tex +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/unicode-math-xetex.sty +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/xsl/convert.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/xsl/xmltohtml.xsl +0 -0
- {finalcif-172 → finalcif-173}/finalcif/template/xsl/xmltolatex.xsl +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/__init__.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/chemparse.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/download.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/dsrmath.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/formal_charge.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/misc.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/mol_file_writer.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/options.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/platon.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/pupdate.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/settings.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/shred.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/space_groups.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/spgr_format.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/squeeze.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/statusbar.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/sumformula.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif/tools/z_from_packing.py +0 -0
- {finalcif-172 → finalcif-173}/finalcif.exe +0 -0
- {finalcif-172 → finalcif-173}/platon/README.txt +0 -0
- {finalcif-172 → finalcif-173}/platon/compile_platon.bat +0 -0
- {finalcif-172 → finalcif-173}/platon/platon_special.exe +0 -0
- {finalcif-172 → finalcif-173}/platon/platon_special.f +0 -0
- {finalcif-172 → finalcif-173}/pylock.toml +0 -0
- {finalcif-172 → finalcif-173}/pyproject.toml +0 -0
- {finalcif-172 → finalcif-173}/run_and_capture.py +0 -0
- {finalcif-172 → finalcif-173}/run_finalcif +0 -0
- {finalcif-172 → finalcif-173}/run_finalcif.bat +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_checkcif.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_details.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_loops.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_loops2.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_main.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_report.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/finalcif_responses.png +0 -0
- {finalcif-172 → finalcif-173}/screenshots/loops_column_buttons.png +0 -0
- {finalcif-172 → finalcif-173}/uv.lock +0 -0
- {finalcif-172 → finalcif-173}/work/compare_z_platon.py +0 -0
- {finalcif-172 → finalcif-173}/work/diagnose_1519506.py +0 -0
- {finalcif-172 → finalcif-173}/work/diagnose_z.py +0 -0
- {finalcif-172 → finalcif-173}/work/investigate_failures.py +0 -0
- {finalcif-172 → finalcif-173}/work/validate_cod.py +0 -0
- {finalcif-172 → finalcif-173}/work/validate_zprime.py +0 -0
|
@@ -1072,6 +1072,7 @@ class AppWindow(QMainWindow):
|
|
|
1072
1072
|
source_item = MyTableWidgetItem(s)
|
|
1073
1073
|
source_item.setUneditable()
|
|
1074
1074
|
data_item = MyTableWidgetItem(self.sources[s][1])
|
|
1075
|
+
data_item.setToolTip(str(self.sources[s][1]))
|
|
1075
1076
|
data_item.setUneditable()
|
|
1076
1077
|
table.setItem(rownum, COL_key, source_item)
|
|
1077
1078
|
table.setItem(rownum, COL_source_data, data_item)
|
|
@@ -2499,7 +2500,8 @@ class AppWindow(QMainWindow):
|
|
|
2499
2500
|
Tries to determine the sources of missing data in the cif file, e.g. Tmin/Tmax from SADABS.
|
|
2500
2501
|
"""
|
|
2501
2502
|
self.check_Z()
|
|
2502
|
-
|
|
2503
|
+
bruker_data = BrukerData(self, self.cif)
|
|
2504
|
+
self.sources = bruker_data.sources
|
|
2503
2505
|
squeeze_dialog_shown = False
|
|
2504
2506
|
if self.sources:
|
|
2505
2507
|
# Add the CCDC number in case we have a deposition mail lying around:
|
|
@@ -2540,6 +2542,20 @@ class AppWindow(QMainWindow):
|
|
|
2540
2542
|
# TypeError my originate from incomplete self.missing_data list!
|
|
2541
2543
|
# print(e, '##', miss_key)
|
|
2542
2544
|
pass
|
|
2545
|
+
self._apply_source_overrides(bruker_data.overrides)
|
|
2546
|
+
|
|
2547
|
+
def _apply_source_overrides(self, overrides: set[str]) -> None:
|
|
2548
|
+
"""
|
|
2549
|
+
Replaces values that are known to be wrong in the CIF (SHELXL writes for example
|
|
2550
|
+
wrong reflection numbers for twins) by the value of the corresponding data source.
|
|
2551
|
+
"""
|
|
2552
|
+
for key in overrides:
|
|
2553
|
+
source = self.sources.get(key)
|
|
2554
|
+
if not source or not source[0] or key in self.missing_data:
|
|
2555
|
+
continue
|
|
2556
|
+
if not self.ui.cif_main_table.has_key(key):
|
|
2557
|
+
continue
|
|
2558
|
+
self.ui.cif_main_table.setText(key=key, column=Column.DATA, txt=str(source[0]), color=light_green)
|
|
2543
2559
|
|
|
2544
2560
|
def add_ccdc_number(self):
|
|
2545
2561
|
ccdc = CCDCMail(self.cif)
|
|
@@ -2550,7 +2566,7 @@ class AppWindow(QMainWindow):
|
|
|
2550
2566
|
self.add_row('_database_code_depnum_ccdc_archive', '', at_start=True)
|
|
2551
2567
|
txt = self.ui.cif_main_table.getTextFromKey('_database_code_depnum_ccdc_archive', Column.EDIT).strip()
|
|
2552
2568
|
if not txt or (txt == '?'):
|
|
2553
|
-
self.sources['_database_code_depnum_ccdc_archive'] = (str(ccdc.depnum), str(ccdc.emlfile
|
|
2569
|
+
self.sources['_database_code_depnum_ccdc_archive'] = (str(ccdc.depnum), str(ccdc.emlfile))
|
|
2554
2570
|
self.missing_data.add('_database_code_depnum_ccdc_archive')
|
|
2555
2571
|
|
|
2556
2572
|
def refresh_combo_boxes(self) -> None:
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections import namedtuple
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import gemmi
|
|
9
|
+
import numpy as np
|
|
10
|
+
from gemmi.cif import Loop, Document, Style
|
|
11
|
+
from packaging import version
|
|
12
|
+
|
|
13
|
+
Limit = namedtuple('Limit', 'h_max, h_min, k_max, k_min, l_max, l_min')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HKL:
|
|
17
|
+
"""
|
|
18
|
+
loop_
|
|
19
|
+
_refln_index_h
|
|
20
|
+
_refln_index_k
|
|
21
|
+
_refln_index_l
|
|
22
|
+
_refln_F_squared_meas
|
|
23
|
+
_refln_F_squared_sigma
|
|
24
|
+
_refln_scale_group_code
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, hkl_file: str, block_name: str, hklf_type: int = 4):
|
|
28
|
+
self._hkl_file = hkl_file
|
|
29
|
+
self.hklf_type = hklf_type
|
|
30
|
+
self._doc: Document = gemmi.cif.Document()
|
|
31
|
+
self._doc.add_new_block(block_name)
|
|
32
|
+
self.block = self._doc.sole_block()
|
|
33
|
+
self._add_hkl_as_loop()
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def hkl_as_cif(self) -> str:
|
|
37
|
+
if version.parse(gemmi.__version__) < version.parse('0.5.1'):
|
|
38
|
+
return self._doc.as_string(style=Style.Simple)
|
|
39
|
+
else:
|
|
40
|
+
return self._doc.as_string(options=gemmi.cif.WriteOptions(Style.Simple))
|
|
41
|
+
|
|
42
|
+
def _add_hkl_as_loop(self) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Adds the hkl data from a SHELX hkl file as local loop.
|
|
45
|
+
"""
|
|
46
|
+
hkl_width = self._get_hkl_width()
|
|
47
|
+
loop_header = ['index_h',
|
|
48
|
+
'index_k',
|
|
49
|
+
'index_l',
|
|
50
|
+
'F_squared_meas' if self.hklf_type != 3 else 'F_meas',
|
|
51
|
+
'F_squared_sigma' if self.hklf_type != 3 else 'F_sigma',
|
|
52
|
+
'scale_group_code']
|
|
53
|
+
loop: Loop = self.block.init_loop('_refln_', self._trim_header_to_hkl_width(loop_header))
|
|
54
|
+
zero_reflection_pattern = re.compile(r'^\s+0\s+0\s+0\s+0.*')
|
|
55
|
+
for line in self._hkl_file.splitlines(keepends=False):
|
|
56
|
+
splitline = line.split()
|
|
57
|
+
if not splitline:
|
|
58
|
+
continue
|
|
59
|
+
# Do not use data after the 0 0 0 reflection
|
|
60
|
+
if zero_reflection_pattern.match(line):
|
|
61
|
+
# Need to truncate, because some programs add the scale group even if
|
|
62
|
+
# there is no scale group in other reflections
|
|
63
|
+
loop.add_row(splitline[:hkl_width])
|
|
64
|
+
break
|
|
65
|
+
try:
|
|
66
|
+
loop.add_row(splitline[:len(loop_header)])
|
|
67
|
+
# RuntimeError ist from gemmi.cif.add_row:
|
|
68
|
+
except (IndexError, RuntimeError):
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
def __repr__(self) -> str:
|
|
72
|
+
return self.hkl_as_cif[:250]
|
|
73
|
+
|
|
74
|
+
def _trim_header_to_hkl_width(self, loop_header: list[str]) -> list[str]:
|
|
75
|
+
hkl_with = self._get_hkl_width()
|
|
76
|
+
trimmed_header = loop_header[:hkl_with]
|
|
77
|
+
return trimmed_header
|
|
78
|
+
|
|
79
|
+
def _get_hkl_width(self) -> int:
|
|
80
|
+
first_lines = self._hkl_file[:150].strip().splitlines(keepends=False)
|
|
81
|
+
if len(first_lines) > 1:
|
|
82
|
+
return len(first_lines[1].split())
|
|
83
|
+
return len(first_lines[0].split())
|
|
84
|
+
|
|
85
|
+
def get_hkl_min_max(self) -> Limit:
|
|
86
|
+
hkl: gemmi.ReflnBlock = gemmi.hkl_cif_as_refln_block(self.block)
|
|
87
|
+
miller = hkl.make_miller_array()
|
|
88
|
+
h_max, k_max, l_max = np.max(miller, axis=0)
|
|
89
|
+
h_min, k_min, l_min = np.min(miller, axis=0)
|
|
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
|
+
|
|
92
|
+
|
|
93
|
+
def calculate_rint(hkl_file: str, space_group: str, cell: Sequence[float] | None = None,
|
|
94
|
+
resolution: tuple[float, float] | None = None) -> float | None:
|
|
95
|
+
"""
|
|
96
|
+
R(int) of an unmerged SHELX HKLF 4 file, calculated the way SHELXL does it:
|
|
97
|
+
|
|
98
|
+
R(int) = sum|Fo^2 - Fo^2(mean)| / sum[Fo^2]
|
|
99
|
+
|
|
100
|
+
Systematically absent reflections are rejected and Friedel opposites are only merged in
|
|
101
|
+
centrosymmetric space groups. Fo^2(mean) is the mean weighted with 1/sigma^2.
|
|
102
|
+
|
|
103
|
+
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.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
hkl_file: Content of a SHELX HKLF 4 file.
|
|
108
|
+
space_group: Space group name, e.g. 'P n a 21'.
|
|
109
|
+
cell: Unit cell parameters, needed to apply a resolution limit.
|
|
110
|
+
resolution: Lowest and highest resolution in Angstrom (a SHELX SHEL instruction).
|
|
111
|
+
"""
|
|
112
|
+
miller, intensities, sigmas = _reflections_from_shelx_hkl(hkl_file)
|
|
113
|
+
if not len(miller) or not space_group:
|
|
114
|
+
return None
|
|
115
|
+
try:
|
|
116
|
+
operations = gemmi.SpaceGroup(space_group).operations()
|
|
117
|
+
except (RuntimeError, ValueError):
|
|
118
|
+
return None
|
|
119
|
+
used = ~_systematically_absent(miller, operations)
|
|
120
|
+
used &= _within_resolution(miller, cell, resolution)
|
|
121
|
+
miller, intensities, sigmas = miller[used], intensities[used], sigmas[used]
|
|
122
|
+
if not len(miller):
|
|
123
|
+
return None
|
|
124
|
+
groups, counts = _group_equivalents(miller, operations)
|
|
125
|
+
if len(counts) >= len(miller):
|
|
126
|
+
# Merged data have no equivalent reflections to compare:
|
|
127
|
+
return None
|
|
128
|
+
return _rint_of_groups(groups, counts, intensities, sigmas)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _within_resolution(miller: np.ndarray, cell: Sequence[float] | None,
|
|
132
|
+
resolution: tuple[float, float] | None) -> np.ndarray:
|
|
133
|
+
"""
|
|
134
|
+
Applies the resolution limits of a SHELX SHEL instruction.
|
|
135
|
+
"""
|
|
136
|
+
if not resolution or not cell:
|
|
137
|
+
return np.ones(len(miller), dtype=bool)
|
|
138
|
+
lowest, highest = max(resolution), min(resolution)
|
|
139
|
+
try:
|
|
140
|
+
spacing = gemmi.UnitCell(*cell[:6]).calculate_d_array(miller)
|
|
141
|
+
except (RuntimeError, TypeError, ValueError):
|
|
142
|
+
return np.ones(len(miller), dtype=bool)
|
|
143
|
+
return (spacing <= lowest) & (spacing >= highest)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _rint_of_groups(groups: np.ndarray, counts: np.ndarray, intensities: np.ndarray,
|
|
147
|
+
sigmas: np.ndarray) -> float | None:
|
|
148
|
+
weights = np.where(sigmas > 0.0, 1.0 / np.square(np.where(sigmas > 0.0, sigmas, 1.0)), 1.0)
|
|
149
|
+
weight_sum = np.bincount(groups, weights=weights, minlength=len(counts))
|
|
150
|
+
mean = np.bincount(groups, weights=weights * intensities, minlength=len(counts)) / weight_sum
|
|
151
|
+
with_equivalents = counts[groups] > 1
|
|
152
|
+
numerator = np.abs(intensities - mean[groups])[with_equivalents].sum()
|
|
153
|
+
denominator = intensities[with_equivalents].sum()
|
|
154
|
+
if not denominator:
|
|
155
|
+
return None
|
|
156
|
+
return round(float(numerator / denominator), 4)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _group_equivalents(miller: np.ndarray, operations: gemmi.GroupOps) -> tuple[np.ndarray, np.ndarray]:
|
|
160
|
+
"""
|
|
161
|
+
Assigns a group number to every reflection that is symmetry equivalent to another one.
|
|
162
|
+
"""
|
|
163
|
+
equivalents = [_encode(miller @ _rotation(op)) for op in operations.sym_ops]
|
|
164
|
+
if operations.is_centrosymmetric():
|
|
165
|
+
equivalents += [_encode(-(miller @ _rotation(op))) for op in operations.sym_ops]
|
|
166
|
+
representative = np.max(np.stack(equivalents), axis=0)
|
|
167
|
+
_, groups, counts = np.unique(representative, return_inverse=True, return_counts=True)
|
|
168
|
+
return groups, counts
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _systematically_absent(miller: np.ndarray, operations: gemmi.GroupOps) -> np.ndarray:
|
|
172
|
+
absent = np.zeros(len(miller), dtype=bool)
|
|
173
|
+
for op in operations.sym_ops:
|
|
174
|
+
translation = np.array(op.tran, dtype=np.float64) / op.DEN
|
|
175
|
+
if not translation.any():
|
|
176
|
+
continue
|
|
177
|
+
unchanged = (miller @ _rotation(op) == miller).all(axis=1)
|
|
178
|
+
phase = miller @ translation
|
|
179
|
+
absent |= unchanged & (np.abs(phase - np.round(phase)) > 1e-6)
|
|
180
|
+
for centering in operations.cen_ops:
|
|
181
|
+
translation = np.array(centering, dtype=np.float64) / gemmi.Op.DEN
|
|
182
|
+
if not translation.any():
|
|
183
|
+
continue
|
|
184
|
+
phase = miller @ translation
|
|
185
|
+
absent |= np.abs(phase - np.round(phase)) > 1e-6
|
|
186
|
+
return absent
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _rotation(op: gemmi.Op) -> np.ndarray:
|
|
190
|
+
"""The rotation part of a symmetry operation, applicable as ``miller @ rotation``."""
|
|
191
|
+
return np.array(op.rot, dtype=np.int64) // op.DEN
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _encode(miller: np.ndarray) -> np.ndarray:
|
|
195
|
+
"""Packs Miller indices into single numbers that keep their lexicographic order."""
|
|
196
|
+
shifted = miller.astype(np.int64) + 2 ** 20
|
|
197
|
+
return (shifted[:, 0] << 42) | (shifted[:, 1] << 21) | shifted[:, 2]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _reflections_from_shelx_hkl(hkl_file: str) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
201
|
+
miller, intensities, sigmas = [], [], []
|
|
202
|
+
for line in hkl_file.splitlines(keepends=False):
|
|
203
|
+
if len(line) < 28:
|
|
204
|
+
continue
|
|
205
|
+
try:
|
|
206
|
+
index = (int(line[0:4]), int(line[4:8]), int(line[8:12]))
|
|
207
|
+
if not any(index):
|
|
208
|
+
break
|
|
209
|
+
intensity, sigma = float(line[12:20]), float(line[20:28])
|
|
210
|
+
except ValueError:
|
|
211
|
+
continue
|
|
212
|
+
miller.append(index)
|
|
213
|
+
intensities.append(intensity)
|
|
214
|
+
sigmas.append(sigma)
|
|
215
|
+
return (np.array(miller, dtype=np.int32).reshape(-1, 3),
|
|
216
|
+
np.array(intensities, dtype=np.float64),
|
|
217
|
+
np.array(sigmas, dtype=np.float64))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == '__main__':
|
|
221
|
+
h = HKL(Path('tests/examples/work/test_hkl_file.txt').read_text(), '123234')
|
|
222
|
+
# print(h.hkl_as_cif[:250])
|
|
223
|
+
m = h.get_hkl_min_max()
|
|
224
|
+
print(m)
|
|
@@ -12,6 +12,7 @@ from pathlib import Path
|
|
|
12
12
|
from gemmi import cif as gcif
|
|
13
13
|
|
|
14
14
|
from finalcif.cif.cif_file_io import CifContainer
|
|
15
|
+
from finalcif.cif.hkl import calculate_rint
|
|
15
16
|
from finalcif.datafiles.bruker_frame import BrukerFrameHeader
|
|
16
17
|
from finalcif.datafiles.data import WorkDataMixin
|
|
17
18
|
from finalcif.datafiles.p4p_reader import P4PFile
|
|
@@ -21,6 +22,20 @@ from finalcif.datafiles.shelx_lst import SolutionProgram
|
|
|
21
22
|
from finalcif.gui.dialogs import show_general_warning
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def source_path(file: Path | str | None) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Returns the absolute path of a data source file or an empty string if it does not exist.
|
|
28
|
+
"""
|
|
29
|
+
if not file:
|
|
30
|
+
return ''
|
|
31
|
+
path = Path(file)
|
|
32
|
+
with suppress(OSError):
|
|
33
|
+
path = path.resolve()
|
|
34
|
+
if path.is_file():
|
|
35
|
+
return str(path)
|
|
36
|
+
return ''
|
|
37
|
+
|
|
38
|
+
|
|
24
39
|
class MissingCifData:
|
|
25
40
|
def __init__(self):
|
|
26
41
|
self.data = {}
|
|
@@ -54,12 +69,13 @@ class BrukerData(WorkDataMixin):
|
|
|
54
69
|
abstype = '?'
|
|
55
70
|
t_min = '?'
|
|
56
71
|
t_max = '?'
|
|
72
|
+
sadabs = self.sadabs
|
|
57
73
|
# Going back from last dataset:
|
|
58
|
-
for n in range(1, len(
|
|
74
|
+
for n in range(1, len(sadabs.datasets) + 1):
|
|
59
75
|
try:
|
|
60
|
-
abstype = 'numerical' if
|
|
61
|
-
t_min =
|
|
62
|
-
t_max =
|
|
76
|
+
abstype = 'numerical' if sadabs.dataset(-n).numerical else 'multi-scan'
|
|
77
|
+
t_min = sadabs.dataset(-n).transmission.tmin
|
|
78
|
+
t_max = sadabs.dataset(-n).transmission.tmax
|
|
63
79
|
if all([abstype, t_min, t_max]):
|
|
64
80
|
break
|
|
65
81
|
except (KeyError, AttributeError, TypeError):
|
|
@@ -80,35 +96,34 @@ class BrukerData(WorkDataMixin):
|
|
|
80
96
|
except (AttributeError, KeyError, FileNotFoundError):
|
|
81
97
|
milliamps = ''
|
|
82
98
|
try:
|
|
83
|
-
frame_name = self.frame_header.filename
|
|
99
|
+
frame_name = source_path(self.frame_header.filename)
|
|
84
100
|
except FileNotFoundError:
|
|
85
101
|
frame_name = ''
|
|
86
102
|
if not self.cif['_computing_structure_solution'] and self.cif.solution_program_details:
|
|
87
|
-
solution_program = (self.cif.solution_program_details, self.cif.fileobj
|
|
103
|
+
solution_program = (self.cif.solution_program_details, source_path(self.cif.fileobj))
|
|
88
104
|
if self.cif['_computing_structure_solution']:
|
|
89
|
-
solution_program = (gcif.as_string(self.cif['_computing_structure_solution']),
|
|
105
|
+
solution_program = (gcif.as_string(self.cif['_computing_structure_solution']),
|
|
106
|
+
source_path(self.cif.fileobj))
|
|
90
107
|
if not solution_program:
|
|
91
|
-
solution_program = (sol.program.version,
|
|
108
|
+
solution_program = (sol.program.version, source_path(sol.program.filename))
|
|
92
109
|
if self.cif.absorpt_process_details:
|
|
93
|
-
absdetails = (self.cif.absorpt_process_details, self.cif.fileobj
|
|
110
|
+
absdetails = (self.cif.absorpt_process_details, source_path(self.cif.fileobj))
|
|
94
111
|
else:
|
|
95
|
-
absdetails = (
|
|
112
|
+
absdetails = (sadabs.version, source_path(sadabs.filename))
|
|
96
113
|
if self.cif.absorpt_correction_type:
|
|
97
|
-
abscorrtype = (self.cif.absorpt_correction_type, self.cif.fileobj
|
|
114
|
+
abscorrtype = (self.cif.absorpt_correction_type, source_path(self.cif.fileobj))
|
|
98
115
|
else:
|
|
99
|
-
abscorrtype = (abstype,
|
|
116
|
+
abscorrtype = (abstype, source_path(sadabs.filename))
|
|
100
117
|
if self.cif.absorpt_correction_t_max:
|
|
101
|
-
abs_tmax = (self.cif.absorpt_correction_t_max, self.cif.fileobj
|
|
118
|
+
abs_tmax = (self.cif.absorpt_correction_t_max, source_path(self.cif.fileobj))
|
|
102
119
|
else:
|
|
103
|
-
abs_tmax = (str(t_max),
|
|
120
|
+
abs_tmax = (str(t_max), source_path(sadabs.filename))
|
|
104
121
|
if self.cif.absorpt_correction_t_min:
|
|
105
|
-
abs_tmin = (self.cif.absorpt_correction_t_min, self.cif.fileobj
|
|
122
|
+
abs_tmin = (self.cif.absorpt_correction_t_min, source_path(self.cif.fileobj))
|
|
106
123
|
else:
|
|
107
|
-
abs_tmin = (str(t_min),
|
|
124
|
+
abs_tmin = (str(t_min), source_path(sadabs.filename))
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
rint = (self.sadabs.Rint, self.sadabs.filename.name)
|
|
111
|
-
self.sources['_diffrn_reflns_av_R_equivalents'] = rint
|
|
126
|
+
self._add_reflection_data(sadabs)
|
|
112
127
|
temp2 = self.p4p.temperature
|
|
113
128
|
if temp1 is not None and temp2 is not None:
|
|
114
129
|
temperature = round(min([temp1, temp2]), 1)
|
|
@@ -164,25 +179,26 @@ class BrukerData(WorkDataMixin):
|
|
|
164
179
|
# All sources that are not filled with data will be yellow in the main table
|
|
165
180
|
# data tooltip
|
|
166
181
|
self.sources['_cell_measurement_reflns_used'] = (
|
|
167
|
-
self.saint_data.cell_reflections, self.saint_data.filename
|
|
182
|
+
self.saint_data.cell_reflections, source_path(self.saint_data.filename))
|
|
168
183
|
self.sources['_cell_measurement_theta_min'] = (
|
|
169
|
-
self.saint_data.cell_res_min_theta or '', self.saint_data.filename
|
|
184
|
+
self.saint_data.cell_res_min_theta or '', source_path(self.saint_data.filename))
|
|
170
185
|
self.sources['_cell_measurement_theta_max'] = (
|
|
171
|
-
self.saint_data.cell_res_max_theta or '', self.saint_data.filename
|
|
172
|
-
self.sources['_computing_data_collection'] = (saint_first_ls.aquire_software,
|
|
173
|
-
|
|
174
|
-
self.sources['
|
|
186
|
+
self.saint_data.cell_res_max_theta or '', source_path(self.saint_data.filename))
|
|
187
|
+
self.sources['_computing_data_collection'] = (saint_first_ls.aquire_software,
|
|
188
|
+
source_path(saint_first_ls.filename))
|
|
189
|
+
self.sources['_computing_cell_refinement'] = (self.saint_data.version, source_path(self.saint_data.filename))
|
|
190
|
+
self.sources['_computing_data_reduction'] = (self.saint_data.version, source_path(self.saint_data.filename))
|
|
175
191
|
self.sources['_exptl_absorpt_correction_type'] = abscorrtype
|
|
176
192
|
self.sources['_exptl_absorpt_correction_T_min'] = abs_tmin
|
|
177
193
|
self.sources['_exptl_absorpt_correction_T_max'] = abs_tmax
|
|
178
194
|
self.sources['_exptl_absorpt_process_details'] = absdetails
|
|
179
|
-
self.sources['_cell_measurement_temperature'] = (temperature, self.p4p.filename
|
|
180
|
-
self.sources['_diffrn_ambient_temperature'] = (temperature, self.p4p.filename
|
|
181
|
-
self.sources['_exptl_crystal_colour'] = (self.p4p.crystal_color, self.p4p.filename
|
|
182
|
-
self.sources['_exptl_crystal_description'] = (self.p4p.morphology, self.p4p.filename
|
|
183
|
-
self.sources['_exptl_crystal_size_min'] = (self.p4p.crystal_size[0] or '', self.p4p.filename
|
|
184
|
-
self.sources['_exptl_crystal_size_mid'] = (self.p4p.crystal_size[1] or '', self.p4p.filename
|
|
185
|
-
self.sources['_exptl_crystal_size_max'] = (self.p4p.crystal_size[2] or '', self.p4p.filename
|
|
195
|
+
self.sources['_cell_measurement_temperature'] = (temperature, source_path(self.p4p.filename))
|
|
196
|
+
self.sources['_diffrn_ambient_temperature'] = (temperature, source_path(self.p4p.filename))
|
|
197
|
+
self.sources['_exptl_crystal_colour'] = (self.p4p.crystal_color, source_path(self.p4p.filename))
|
|
198
|
+
self.sources['_exptl_crystal_description'] = (self.p4p.morphology, source_path(self.p4p.filename))
|
|
199
|
+
self.sources['_exptl_crystal_size_min'] = (self.p4p.crystal_size[0] or '', source_path(self.p4p.filename))
|
|
200
|
+
self.sources['_exptl_crystal_size_mid'] = (self.p4p.crystal_size[1] or '', source_path(self.p4p.filename))
|
|
201
|
+
self.sources['_exptl_crystal_size_max'] = (self.p4p.crystal_size[2] or '', source_path(self.p4p.filename))
|
|
186
202
|
self.sources['_computing_structure_solution'] = solution_program
|
|
187
203
|
self.sources['_atom_sites_solution_primary'] = (sol.method, 'Inherited from solution program.')
|
|
188
204
|
self.sources['_diffrn_source_voltage'] = (kilovolt or '', frame_name)
|
|
@@ -196,19 +212,81 @@ class BrukerData(WorkDataMixin):
|
|
|
196
212
|
if self.saint_data.is_twin and self.saint_data.components_firstsample == 2:
|
|
197
213
|
with suppress(Exception):
|
|
198
214
|
law = self.saint_data.twinlaw[next(iter(self.saint_data.twinlaw.keys()))]
|
|
199
|
-
|
|
200
|
-
self.sources['
|
|
201
|
-
self.sources['
|
|
202
|
-
self.sources['
|
|
203
|
-
self.sources['
|
|
204
|
-
self.sources['
|
|
205
|
-
self.sources['
|
|
206
|
-
self.sources['
|
|
207
|
-
self.sources['
|
|
215
|
+
saint_file = source_path(self.saint_data.filename)
|
|
216
|
+
self.sources['_twin_individual_twin_matrix_11'] = (str(law[0][1]), saint_file)
|
|
217
|
+
self.sources['_twin_individual_twin_matrix_12'] = (str(law[0][2]), saint_file)
|
|
218
|
+
self.sources['_twin_individual_twin_matrix_13'] = (str(law[0][0]), saint_file)
|
|
219
|
+
self.sources['_twin_individual_twin_matrix_21'] = (str(law[1][1]), saint_file)
|
|
220
|
+
self.sources['_twin_individual_twin_matrix_22'] = (str(law[1][2]), saint_file)
|
|
221
|
+
self.sources['_twin_individual_twin_matrix_23'] = (str(law[1][0]), saint_file)
|
|
222
|
+
self.sources['_twin_individual_twin_matrix_31'] = (str(law[2][1]), saint_file)
|
|
223
|
+
self.sources['_twin_individual_twin_matrix_32'] = (str(law[2][2]), saint_file)
|
|
224
|
+
self.sources['_twin_individual_twin_matrix_33'] = (str(law[2][0]), saint_file)
|
|
208
225
|
self.sources['_twin_individual_id'] = (
|
|
209
|
-
str(self.saint_data.components_firstsample),
|
|
226
|
+
str(self.saint_data.components_firstsample), saint_file)
|
|
210
227
|
self.sources['_twin_special_details'] = (
|
|
211
|
-
'The data was integrated as a 2-component twin.',
|
|
228
|
+
'The data was integrated as a 2-component twin.', saint_file)
|
|
229
|
+
|
|
230
|
+
def _add_reflection_data(self, sadabs: Sadabs) -> None:
|
|
231
|
+
"""
|
|
232
|
+
Adds the number of measured reflections and R(int) of the data set that belongs to the
|
|
233
|
+
hkl file of this refinement. SHELXL can not determine these values from an HKLF 5 file,
|
|
234
|
+
thus the values of a TWINABS file take precedence over the values in the CIF.
|
|
235
|
+
"""
|
|
236
|
+
dataset = sadabs.select_dataset(hkl_basename=self._hkl_basename,
|
|
237
|
+
reflections=self._reflections_in_hkl_file(),
|
|
238
|
+
hklf=self.cif.hklf_number)
|
|
239
|
+
if not dataset:
|
|
240
|
+
return
|
|
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:
|
|
245
|
+
self.sources['_diffrn_reflns_number'] = (dataset.reflections_number, source_path(sadabs.filename))
|
|
246
|
+
if sadabs.is_twinabs and dataset.filetype == 5:
|
|
247
|
+
self.overrides.update({'_diffrn_reflns_av_R_equivalents', '_diffrn_reflns_number'})
|
|
248
|
+
|
|
249
|
+
def _rint_source(self, dataset, sadabs: Sadabs) -> str:
|
|
250
|
+
if dataset.rint:
|
|
251
|
+
return source_path(sadabs.filename)
|
|
252
|
+
return f'calculated from {source_path(self.cif.fileobj)}'
|
|
253
|
+
|
|
254
|
+
def _rint_from_hkl_data(self) -> float | None:
|
|
255
|
+
"""
|
|
256
|
+
SADABS writes no overall R(int) into its listing file, therefore it is calculated from
|
|
257
|
+
the reflection data, but only if the CIF has no R(int) yet. SHELXL can not calculate it
|
|
258
|
+
for the composite reflections of HKLF 5 data.
|
|
259
|
+
"""
|
|
260
|
+
if self.cif.hklf_number != 4:
|
|
261
|
+
return None
|
|
262
|
+
if gcif.as_string(self.cif['_diffrn_reflns_av_R_equivalents']).strip(' ?'):
|
|
263
|
+
return None
|
|
264
|
+
return calculate_rint(self.cif.hkl_file, self.cif.space_group,
|
|
265
|
+
cell=self.cif.cell, resolution=self._resolution_limits())
|
|
266
|
+
|
|
267
|
+
def _resolution_limits(self) -> tuple[float, float] | None:
|
|
268
|
+
"""The resolution limits of a SHELX SHEL instruction, if there is one."""
|
|
269
|
+
shel = getattr(self.cif.shx, 'shel', None)
|
|
270
|
+
if not shel:
|
|
271
|
+
return None
|
|
272
|
+
with suppress(AttributeError, TypeError):
|
|
273
|
+
return float(shel.lowres), float(shel.highres)
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def _hkl_basename(self) -> str:
|
|
278
|
+
return self.cif.fileobj.stem.replace('-finalcif', '')
|
|
279
|
+
|
|
280
|
+
def _reflections_in_hkl_file(self) -> int | None:
|
|
281
|
+
"""
|
|
282
|
+
The number of reflections in the hkl file of this refinement, without the terminating
|
|
283
|
+
0 0 0 reflection and the SADABS/TWINABS footer.
|
|
284
|
+
"""
|
|
285
|
+
hkl = self.cif.hkl_file_without_foot
|
|
286
|
+
if not hkl:
|
|
287
|
+
return None
|
|
288
|
+
lines = [x for x in hkl.splitlines(keepends=False) if x.strip()]
|
|
289
|
+
return len(lines) - 1 if lines else None
|
|
212
290
|
|
|
213
291
|
@property
|
|
214
292
|
def sadabs(self):
|
|
@@ -13,6 +13,8 @@ class WorkDataMixin:
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
def __init__(self):
|
|
16
|
+
# Keys whose source value replaces an already existing (wrong) value in the CIF:
|
|
17
|
+
self.overrides: set[str] = set()
|
|
16
18
|
self.sources = {'_atom_sites_solution_primary' : None,
|
|
17
19
|
'_cell_measurement_reflns_used' : None,
|
|
18
20
|
'_cell_measurement_temperature' : None,
|
|
@@ -27,6 +29,7 @@ class WorkDataMixin:
|
|
|
27
29
|
'_database_code_depnum_ccdc_archive' : None,
|
|
28
30
|
'_diffrn_ambient_temperature' : None,
|
|
29
31
|
'_diffrn_reflns_av_R_equivalents' : None,
|
|
32
|
+
'_diffrn_reflns_number' : None,
|
|
30
33
|
'_diffrn_source_voltage' : None,
|
|
31
34
|
'_diffrn_source_current' : None,
|
|
32
35
|
'_exptl_absorpt_correction_type' : None,
|