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.
Files changed (201) hide show
  1. {finalcif-172 → finalcif-173}/PKG-INFO +1 -1
  2. finalcif-173/finalcif/__init__.py +3 -0
  3. {finalcif-172 → finalcif-173}/finalcif/appwindow.py +18 -2
  4. finalcif-173/finalcif/cif/hkl.py +224 -0
  5. {finalcif-172 → finalcif-173}/finalcif/datafiles/bruker_data.py +121 -43
  6. {finalcif-172 → finalcif-173}/finalcif/datafiles/data.py +3 -0
  7. finalcif-173/finalcif/datafiles/sadabs.py +412 -0
  8. {finalcif-172 → finalcif-173}/finalcif/datafiles/saint.py +52 -53
  9. {finalcif-172 → finalcif-173}/finalcif/gui/dialogs.py +1 -1
  10. {finalcif-172 → finalcif-173}/finalcif/template/report_fastmolwidget.tmpl +11 -1
  11. {finalcif-172 → finalcif-173}/finalcif/tools/selfupdate.py +129 -16
  12. finalcif-172/finalcif/__init__.py +0 -3
  13. finalcif-172/finalcif/cif/hkl.py +0 -94
  14. finalcif-172/finalcif/datafiles/sadabs.py +0 -191
  15. {finalcif-172 → finalcif-173}/.gitattributes +0 -0
  16. {finalcif-172 → finalcif-173}/.githooks/pre-push +0 -0
  17. {finalcif-172 → finalcif-173}/.gitignore +0 -0
  18. {finalcif-172 → finalcif-173}/.python-version +0 -0
  19. {finalcif-172 → finalcif-173}/.ruff.toml +0 -0
  20. {finalcif-172 → finalcif-173}/.run/Make Documentation MAC.run.xml +0 -0
  21. {finalcif-172 → finalcif-173}/.run/compile_ui_files.run.xml +0 -0
  22. {finalcif-172 → finalcif-173}/.run/make documentation Windows.run.xml +0 -0
  23. {finalcif-172 → finalcif-173}/AGENTS.md +0 -0
  24. {finalcif-172 → finalcif-173}/CODE_OF_CONDUCT.md +0 -0
  25. {finalcif-172 → finalcif-173}/CONTRIBUTING.md +0 -0
  26. {finalcif-172 → finalcif-173}/Finalcif_installer_win.spec +0 -0
  27. {finalcif-172 → finalcif-173}/Finalcif_linux_onefile.spec +0 -0
  28. {finalcif-172 → finalcif-173}/Finalcif_mac.spec +0 -0
  29. {finalcif-172 → finalcif-173}/LICENSE +0 -0
  30. {finalcif-172 → finalcif-173}/README.md +0 -0
  31. {finalcif-172 → finalcif-173}/finalcif/app_path.py +0 -0
  32. {finalcif-172 → finalcif-173}/finalcif/cif/__init__.py +0 -0
  33. {finalcif-172 → finalcif-173}/finalcif/cif/all_cif_dicts.py +0 -0
  34. {finalcif-172 → finalcif-173}/finalcif/cif/atoms.py +0 -0
  35. {finalcif-172 → finalcif-173}/finalcif/cif/checkcif/__init__.py +0 -0
  36. {finalcif-172 → finalcif-173}/finalcif/cif/checkcif/checkcif.py +0 -0
  37. {finalcif-172 → finalcif-173}/finalcif/cif/cif_dict_foo.py +0 -0
  38. {finalcif-172 → finalcif-173}/finalcif/cif/cif_file_io.py +0 -0
  39. {finalcif-172 → finalcif-173}/finalcif/cif/cif_keyword_mapping.py +0 -0
  40. {finalcif-172 → finalcif-173}/finalcif/cif/cif_order.py +0 -0
  41. {finalcif-172 → finalcif-173}/finalcif/cif/cod/__init__.py +0 -0
  42. {finalcif-172 → finalcif-173}/finalcif/cif/cod/deposit.py +0 -0
  43. {finalcif-172 → finalcif-173}/finalcif/cif/cod/deposit_check.py +0 -0
  44. {finalcif-172 → finalcif-173}/finalcif/cif/cod/deposition_list.py +0 -0
  45. {finalcif-172 → finalcif-173}/finalcif/cif/cod/doi.py +0 -0
  46. {finalcif-172 → finalcif-173}/finalcif/cif/cod/upload.py +0 -0
  47. {finalcif-172 → finalcif-173}/finalcif/cif/cod/website_parser.py +0 -0
  48. {finalcif-172 → finalcif-173}/finalcif/cif/core_dict.py +0 -0
  49. {finalcif-172 → finalcif-173}/finalcif/cif/modulation_dict.py +0 -0
  50. {finalcif-172 → finalcif-173}/finalcif/cif/powder_dict.py +0 -0
  51. {finalcif-172 → finalcif-173}/finalcif/cif/reference.py +0 -0
  52. {finalcif-172 → finalcif-173}/finalcif/cif/restraints_dict.py +0 -0
  53. {finalcif-172 → finalcif-173}/finalcif/cif/text.py +0 -0
  54. {finalcif-172 → finalcif-173}/finalcif/cif/twin_dict.py +0 -0
  55. {finalcif-172 → finalcif-173}/finalcif/cif/vrf_entry.py +0 -0
  56. {finalcif-172 → finalcif-173}/finalcif/ciforder/__init__.py +0 -0
  57. {finalcif-172 → finalcif-173}/finalcif/ciforder/order.py +0 -0
  58. {finalcif-172 → finalcif-173}/finalcif/ciforder/order_ui.py +0 -0
  59. {finalcif-172 → finalcif-173}/finalcif/ciforder/order_ui.ui +0 -0
  60. {finalcif-172 → finalcif-173}/finalcif/datafiles/__init__.py +0 -0
  61. {finalcif-172 → finalcif-173}/finalcif/datafiles/bruker_frame.py +0 -0
  62. {finalcif-172 → finalcif-173}/finalcif/datafiles/ccdc_mail.py +0 -0
  63. {finalcif-172 → finalcif-173}/finalcif/datafiles/p4p_reader.py +0 -0
  64. {finalcif-172 → finalcif-173}/finalcif/datafiles/shelx_lst.py +0 -0
  65. {finalcif-172 → finalcif-173}/finalcif/datafiles/utils.py +0 -0
  66. {finalcif-172 → finalcif-173}/finalcif/equip_property/__init__.py +0 -0
  67. {finalcif-172 → finalcif-173}/finalcif/equip_property/author_loop_templates.py +0 -0
  68. {finalcif-172 → finalcif-173}/finalcif/equip_property/equipment.py +0 -0
  69. {finalcif-172 → finalcif-173}/finalcif/equip_property/properties.py +0 -0
  70. {finalcif-172 → finalcif-173}/finalcif/equip_property/tools.py +0 -0
  71. {finalcif-172 → finalcif-173}/finalcif/finalcif_start.py +0 -0
  72. {finalcif-172 → finalcif-173}/finalcif/gui/__init__.py +0 -0
  73. {finalcif-172 → finalcif-173}/finalcif/gui/block_combobox.py +0 -0
  74. {finalcif-172 → finalcif-173}/finalcif/gui/checkcif_browser.py +0 -0
  75. {finalcif-172 → finalcif-173}/finalcif/gui/cif_table_model.py +0 -0
  76. {finalcif-172 → finalcif-173}/finalcif/gui/cif_table_view.py +0 -0
  77. {finalcif-172 → finalcif-173}/finalcif/gui/combobox.py +0 -0
  78. {finalcif-172 → finalcif-173}/finalcif/gui/custom_classes.py +0 -0
  79. {finalcif-172 → finalcif-173}/finalcif/gui/edit_button.py +0 -0
  80. {finalcif-172 → finalcif-173}/finalcif/gui/equipmenttable.py +0 -0
  81. {finalcif-172 → finalcif-173}/finalcif/gui/file_editor.py +0 -0
  82. {finalcif-172 → finalcif-173}/finalcif/gui/finalcif_gui_ui.py +0 -0
  83. {finalcif-172 → finalcif-173}/finalcif/gui/finalcif_gui_ui.ui +0 -0
  84. {finalcif-172 → finalcif-173}/finalcif/gui/import_selector.py +0 -0
  85. {finalcif-172 → finalcif-173}/finalcif/gui/import_selector_ui.py +0 -0
  86. {finalcif-172 → finalcif-173}/finalcif/gui/import_selector_ui.ui +0 -0
  87. {finalcif-172 → finalcif-173}/finalcif/gui/loop_creator.py +0 -0
  88. {finalcif-172 → finalcif-173}/finalcif/gui/loop_creator_ui.py +0 -0
  89. {finalcif-172 → finalcif-173}/finalcif/gui/loop_creator_ui.ui +0 -0
  90. {finalcif-172 → finalcif-173}/finalcif/gui/loops.py +0 -0
  91. {finalcif-172 → finalcif-173}/finalcif/gui/mainstackwidget.py +0 -0
  92. {finalcif-172 → finalcif-173}/finalcif/gui/mixins.py +0 -0
  93. {finalcif-172 → finalcif-173}/finalcif/gui/new_key_dialog.py +0 -0
  94. {finalcif-172 → finalcif-173}/finalcif/gui/new_key_dialog_ui.py +0 -0
  95. {finalcif-172 → finalcif-173}/finalcif/gui/new_key_dialog_ui.ui +0 -0
  96. {finalcif-172 → finalcif-173}/finalcif/gui/plaintextedit.py +0 -0
  97. {finalcif-172 → finalcif-173}/finalcif/gui/playground.py +0 -0
  98. {finalcif-172 → finalcif-173}/finalcif/gui/propertytable.py +0 -0
  99. {finalcif-172 → finalcif-173}/finalcif/gui/shelx_navigation.py +0 -0
  100. {finalcif-172 → finalcif-173}/finalcif/gui/single_line_textedit.py +0 -0
  101. {finalcif-172 → finalcif-173}/finalcif/gui/spell_check_edit.py +0 -0
  102. {finalcif-172 → finalcif-173}/finalcif/gui/squeeze_dialog.py +0 -0
  103. {finalcif-172 → finalcif-173}/finalcif/gui/syntax_highlighter.py +0 -0
  104. {finalcif-172 → finalcif-173}/finalcif/gui/text_templates_ui.py +0 -0
  105. {finalcif-172 → finalcif-173}/finalcif/gui/text_templates_ui.ui +0 -0
  106. {finalcif-172 → finalcif-173}/finalcif/gui/text_value_editor.py +0 -0
  107. {finalcif-172 → finalcif-173}/finalcif/gui/timers.py +0 -0
  108. {finalcif-172 → finalcif-173}/finalcif/gui/validators.py +0 -0
  109. {finalcif-172 → finalcif-173}/finalcif/gui/vrf_classes.py +0 -0
  110. {finalcif-172 → finalcif-173}/finalcif/gui/vzs_viewer.py +0 -0
  111. {finalcif-172 → finalcif-173}/finalcif/icon/__init__.py +0 -0
  112. {finalcif-172 → finalcif-173}/finalcif/icon/finalcif.png +0 -0
  113. {finalcif-172 → finalcif-173}/finalcif/icon/finalcif2.ico +0 -0
  114. {finalcif-172 → finalcif-173}/finalcif/icon/finalcif2.png +0 -0
  115. {finalcif-172 → finalcif-173}/finalcif/icon/multitable.ico +0 -0
  116. {finalcif-172 → finalcif-173}/finalcif/icon/multitable.png +0 -0
  117. {finalcif-172 → finalcif-173}/finalcif/icon/multitable.xcf +0 -0
  118. {finalcif-172 → finalcif-173}/finalcif/report/__init__.py +0 -0
  119. {finalcif-172 → finalcif-173}/finalcif/report/archive_report.py +0 -0
  120. {finalcif-172 → finalcif-173}/finalcif/report/gui/__init__.py +0 -0
  121. {finalcif-172 → finalcif-173}/finalcif/report/gui/mainwindow.py +0 -0
  122. {finalcif-172 → finalcif-173}/finalcif/report/gui/mainwindow.ui +0 -0
  123. {finalcif-172 → finalcif-173}/finalcif/report/mtools.py +0 -0
  124. {finalcif-172 → finalcif-173}/finalcif/report/references.py +0 -0
  125. {finalcif-172 → finalcif-173}/finalcif/report/report_text.py +0 -0
  126. {finalcif-172 → finalcif-173}/finalcif/report/symm.py +0 -0
  127. {finalcif-172 → finalcif-173}/finalcif/report/tables.py +0 -0
  128. {finalcif-172 → finalcif-173}/finalcif/report/templated_report.py +0 -0
  129. {finalcif-172 → finalcif-173}/finalcif/template/__init__.py +0 -0
  130. {finalcif-172 → finalcif-173}/finalcif/template/bootstrap/bootstrap.min.css +0 -0
  131. {finalcif-172 → finalcif-173}/finalcif/template/machines/D8_VENTURE.cif +0 -0
  132. {finalcif-172 → finalcif-173}/finalcif/template/mathml2omml.xsl +0 -0
  133. {finalcif-172 → finalcif-173}/finalcif/template/miew/LICENSES +0 -0
  134. {finalcif-172 → finalcif-173}/finalcif/template/miew/Miew.css +0 -0
  135. {finalcif-172 → finalcif-173}/finalcif/template/miew/Miew.min.js +0 -0
  136. {finalcif-172 → finalcif-173}/finalcif/template/miew/lodash.min.js +0 -0
  137. {finalcif-172 → finalcif-173}/finalcif/template/miew/three.js +0 -0
  138. {finalcif-172 → finalcif-173}/finalcif/template/report.tex +0 -0
  139. {finalcif-172 → finalcif-173}/finalcif/template/report.tmpl +0 -0
  140. {finalcif-172 → finalcif-173}/finalcif/template/report2.tex +0 -0
  141. {finalcif-172 → finalcif-173}/finalcif/template/report_default.docx +0 -0
  142. {finalcif-172 → finalcif-173}/finalcif/template/template1.docx +0 -0
  143. {finalcif-172 → finalcif-173}/finalcif/template/template_for_multitable.docx +0 -0
  144. {finalcif-172 → finalcif-173}/finalcif/template/template_text.docx +0 -0
  145. {finalcif-172 → finalcif-173}/finalcif/template/template_without_text.docx +0 -0
  146. {finalcif-172 → finalcif-173}/finalcif/template/templates.py +0 -0
  147. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/FakeContext.py +0 -0
  148. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/FakeTokenizer.py +0 -0
  149. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/README.md +0 -0
  150. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/FakePlasTeX/TokenizerPassThru.py +0 -0
  151. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/LICENSE +0 -0
  152. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/__init__.py +0 -0
  153. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/u2l.py +0 -0
  154. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/unicode-math-table.tex +0 -0
  155. {finalcif-172 → finalcif-173}/finalcif/template/unicode2latex/unicode-math-xetex.sty +0 -0
  156. {finalcif-172 → finalcif-173}/finalcif/template/xsl/convert.py +0 -0
  157. {finalcif-172 → finalcif-173}/finalcif/template/xsl/xmltohtml.xsl +0 -0
  158. {finalcif-172 → finalcif-173}/finalcif/template/xsl/xmltolatex.xsl +0 -0
  159. {finalcif-172 → finalcif-173}/finalcif/tools/__init__.py +0 -0
  160. {finalcif-172 → finalcif-173}/finalcif/tools/chemparse.py +0 -0
  161. {finalcif-172 → finalcif-173}/finalcif/tools/download.py +0 -0
  162. {finalcif-172 → finalcif-173}/finalcif/tools/dsrmath.py +0 -0
  163. {finalcif-172 → finalcif-173}/finalcif/tools/formal_charge.py +0 -0
  164. {finalcif-172 → finalcif-173}/finalcif/tools/misc.py +0 -0
  165. {finalcif-172 → finalcif-173}/finalcif/tools/mol_file_writer.py +0 -0
  166. {finalcif-172 → finalcif-173}/finalcif/tools/options.py +0 -0
  167. {finalcif-172 → finalcif-173}/finalcif/tools/platon.py +0 -0
  168. {finalcif-172 → finalcif-173}/finalcif/tools/pupdate.py +0 -0
  169. {finalcif-172 → finalcif-173}/finalcif/tools/settings.py +0 -0
  170. {finalcif-172 → finalcif-173}/finalcif/tools/shred.py +0 -0
  171. {finalcif-172 → finalcif-173}/finalcif/tools/space_groups.py +0 -0
  172. {finalcif-172 → finalcif-173}/finalcif/tools/spgr_format.py +0 -0
  173. {finalcif-172 → finalcif-173}/finalcif/tools/squeeze.py +0 -0
  174. {finalcif-172 → finalcif-173}/finalcif/tools/statusbar.py +0 -0
  175. {finalcif-172 → finalcif-173}/finalcif/tools/sumformula.py +0 -0
  176. {finalcif-172 → finalcif-173}/finalcif/tools/z_from_packing.py +0 -0
  177. {finalcif-172 → finalcif-173}/finalcif.exe +0 -0
  178. {finalcif-172 → finalcif-173}/platon/README.txt +0 -0
  179. {finalcif-172 → finalcif-173}/platon/compile_platon.bat +0 -0
  180. {finalcif-172 → finalcif-173}/platon/platon_special.exe +0 -0
  181. {finalcif-172 → finalcif-173}/platon/platon_special.f +0 -0
  182. {finalcif-172 → finalcif-173}/pylock.toml +0 -0
  183. {finalcif-172 → finalcif-173}/pyproject.toml +0 -0
  184. {finalcif-172 → finalcif-173}/run_and_capture.py +0 -0
  185. {finalcif-172 → finalcif-173}/run_finalcif +0 -0
  186. {finalcif-172 → finalcif-173}/run_finalcif.bat +0 -0
  187. {finalcif-172 → finalcif-173}/screenshots/finalcif_checkcif.png +0 -0
  188. {finalcif-172 → finalcif-173}/screenshots/finalcif_details.png +0 -0
  189. {finalcif-172 → finalcif-173}/screenshots/finalcif_loops.png +0 -0
  190. {finalcif-172 → finalcif-173}/screenshots/finalcif_loops2.png +0 -0
  191. {finalcif-172 → finalcif-173}/screenshots/finalcif_main.png +0 -0
  192. {finalcif-172 → finalcif-173}/screenshots/finalcif_report.png +0 -0
  193. {finalcif-172 → finalcif-173}/screenshots/finalcif_responses.png +0 -0
  194. {finalcif-172 → finalcif-173}/screenshots/loops_column_buttons.png +0 -0
  195. {finalcif-172 → finalcif-173}/uv.lock +0 -0
  196. {finalcif-172 → finalcif-173}/work/compare_z_platon.py +0 -0
  197. {finalcif-172 → finalcif-173}/work/diagnose_1519506.py +0 -0
  198. {finalcif-172 → finalcif-173}/work/diagnose_z.py +0 -0
  199. {finalcif-172 → finalcif-173}/work/investigate_failures.py +0 -0
  200. {finalcif-172 → finalcif-173}/work/validate_cod.py +0 -0
  201. {finalcif-172 → finalcif-173}/work/validate_zprime.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: finalcif
3
- Version: 172
3
+ Version: 173
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
@@ -0,0 +1,3 @@
1
+
2
+ VERSION = 173
3
+ __version__ = VERSION
@@ -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
- self.sources = BrukerData(self, self.cif).sources
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.name))
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(self.sadabs.datasets) + 1):
74
+ for n in range(1, len(sadabs.datasets) + 1):
59
75
  try:
60
- abstype = 'numerical' if self.sadabs.dataset(-n).numerical else 'multi-scan'
61
- t_min = self.sadabs.dataset(-n).transmission.tmin
62
- t_max = self.sadabs.dataset(-n).transmission.tmax
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.name
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.name)
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']), self.cif.fileobj.name)
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, Path(sol.program.filename).name)
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.name)
110
+ absdetails = (self.cif.absorpt_process_details, source_path(self.cif.fileobj))
94
111
  else:
95
- absdetails = (self.sadabs.version, self.sadabs.filename.name)
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.name)
114
+ abscorrtype = (self.cif.absorpt_correction_type, source_path(self.cif.fileobj))
98
115
  else:
99
- abscorrtype = (abstype, self.sadabs.filename.name)
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.name)
118
+ abs_tmax = (self.cif.absorpt_correction_t_max, source_path(self.cif.fileobj))
102
119
  else:
103
- abs_tmax = (str(t_max), self.sadabs.filename.name)
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.name)
122
+ abs_tmin = (self.cif.absorpt_correction_t_min, source_path(self.cif.fileobj))
106
123
  else:
107
- abs_tmin = (str(t_min), self.sadabs.filename.name)
124
+ abs_tmin = (str(t_min), source_path(sadabs.filename))
108
125
 
109
- if self.sadabs.Rint:
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.name)
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.name)
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.name)
172
- self.sources['_computing_data_collection'] = (saint_first_ls.aquire_software, saint_first_ls.filename.name)
173
- self.sources['_computing_cell_refinement'] = (self.saint_data.version, self.saint_data.filename.name)
174
- self.sources['_computing_data_reduction'] = (self.saint_data.version, self.saint_data.filename.name)
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.name)
180
- self.sources['_diffrn_ambient_temperature'] = (temperature, self.p4p.filename.name)
181
- self.sources['_exptl_crystal_colour'] = (self.p4p.crystal_color, self.p4p.filename.name)
182
- self.sources['_exptl_crystal_description'] = (self.p4p.morphology, self.p4p.filename.name)
183
- self.sources['_exptl_crystal_size_min'] = (self.p4p.crystal_size[0] or '', self.p4p.filename.name)
184
- self.sources['_exptl_crystal_size_mid'] = (self.p4p.crystal_size[1] or '', self.p4p.filename.name)
185
- self.sources['_exptl_crystal_size_max'] = (self.p4p.crystal_size[2] or '', self.p4p.filename.name)
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
- self.sources['_twin_individual_twin_matrix_11'] = (str(law[0][1]), self.saint_data.filename.name)
200
- self.sources['_twin_individual_twin_matrix_12'] = (str(law[0][2]), self.saint_data.filename.name)
201
- self.sources['_twin_individual_twin_matrix_13'] = (str(law[0][0]), self.saint_data.filename.name)
202
- self.sources['_twin_individual_twin_matrix_21'] = (str(law[1][1]), self.saint_data.filename.name)
203
- self.sources['_twin_individual_twin_matrix_22'] = (str(law[1][2]), self.saint_data.filename.name)
204
- self.sources['_twin_individual_twin_matrix_23'] = (str(law[1][0]), self.saint_data.filename.name)
205
- self.sources['_twin_individual_twin_matrix_31'] = (str(law[2][1]), self.saint_data.filename.name)
206
- self.sources['_twin_individual_twin_matrix_32'] = (str(law[2][2]), self.saint_data.filename.name)
207
- self.sources['_twin_individual_twin_matrix_33'] = (str(law[2][0]), self.saint_data.filename.name)
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), self.saint_data.filename.name)
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.', self.saint_data.filename.name)
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,