xrayscatteringtools 0.1.1__tar.gz → 0.2.1__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 (46) hide show
  1. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/.gitattributes +2 -2
  2. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/LICENSE +1 -1
  3. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/PKG-INFO +28 -4
  4. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/README.md +27 -3
  5. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/pyproject.toml +1 -1
  6. xrayscatteringtools-0.2.1/src/xrayscatteringtools/__init__.py +5 -0
  7. xrayscatteringtools-0.2.1/src/xrayscatteringtools/calib/__init__.py +2 -0
  8. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/calib/geometry_calibration.py +69 -8
  9. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/calib/scattering_corrections.py +2 -2
  10. xrayscatteringtools-0.2.1/src/xrayscatteringtools/data/Jungfrau.h5 +0 -0
  11. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/io.py +114 -6
  12. xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/__init__.py +7 -0
  13. xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/geometries/SF6__CCSD_T_DHK__aug_cc_pV5Z_DK.h5 +0 -0
  14. xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/geometries/make_geometry_h5.py +38 -0
  15. {xrayscatteringtools-0.1.1/tests → xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/patterns}/convert_ab_initio_to_h5.py +1 -1
  16. xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/geometries.py +92 -0
  17. xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/geometries.pyi +17 -0
  18. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/theory/iam.py +61 -29
  19. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/theory/patterns.py +6 -6
  20. xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/patterns.pyi +18 -0
  21. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/utils.py +94 -10
  22. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/DMP_D.xyz +24 -24
  23. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/DMP_L1.xyz +24 -24
  24. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/DMP_L2.xyz +24 -24
  25. xrayscatteringtools-0.2.1/tests/azav_test.ipynb +107 -0
  26. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/calib_test.ipynb +39 -10
  27. xrayscatteringtools-0.2.1/tests/geometries_test.ipynb +44 -0
  28. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/iam_oriented_test.ipynb +21 -11
  29. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/iam_pattern_test.ipynb +12 -12
  30. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/patterns_test.ipynb +43 -47
  31. xrayscatteringtools-0.1.1/src/xrayscatteringtools/__init__.py +0 -3
  32. xrayscatteringtools-0.1.1/src/xrayscatteringtools/theory/__init__.py +0 -1
  33. xrayscatteringtools-0.1.1/tests/__init__.py +0 -0
  34. xrayscatteringtools-0.1.1/tests/sf6_ground.xyz +0 -9
  35. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/.gitignore +0 -0
  36. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/calib/data/Zn_attenuation_length.h5 +0 -0
  37. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/epicsArch.py +0 -0
  38. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/src/xrayscatteringtools/plotting.py +0 -0
  39. {xrayscatteringtools-0.1.1/src/xrayscatteringtools/theory → xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/IAM}/Compton_Factors.npy +0 -0
  40. {xrayscatteringtools-0.1.1/src/xrayscatteringtools/theory → xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/IAM}/Scattering_Factors.npy +0 -0
  41. /xrayscatteringtools-0.1.1/src/xrayscatteringtools/theory/data/SF6_CCSD_aug_cc_pVDZ.h5 → /xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/patterns/SF6__CCSD__aug_cc_pVDZ.h5 +0 -0
  42. /xrayscatteringtools-0.1.1/src/xrayscatteringtools/theory/data/SF6_HF_aug_cc_pVDZ.h5 → /xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/patterns/SF6__HF__aug_cc_pVDZ.h5 +0 -0
  43. /xrayscatteringtools-0.1.1/src/xrayscatteringtools/theory/data/SF6_MP2_aug_cc_pVDZ.h5 → /xrayscatteringtools-0.2.1/src/xrayscatteringtools/theory/data/patterns/SF6__MP2__aug_cc_pVDZ.h5 +0 -0
  44. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/DMP_ground.xyz +0 -0
  45. {xrayscatteringtools-0.1.1/src/xrayscatteringtools/calib → xrayscatteringtools-0.2.1/tests}/__init__.py +0 -0
  46. {xrayscatteringtools-0.1.1 → xrayscatteringtools-0.2.1}/tests/sf6_test.xyz +0 -0
@@ -1,2 +1,2 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrayscatteringtools
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: A library for gas phase analysis at the CXI endstation
5
5
  Project-URL: Homepage, https://github.com/Weber-Group/xrayscatteringtools
6
6
  Project-URL: Issues, https://github.com/Weber-Group/xrayscatteringtools/issues
@@ -29,15 +29,22 @@ Description-Content-Type: text/markdown
29
29
 
30
30
  ## A python library for the analysis of data from the CXI endstation at the LCLS.
31
31
 
32
+ ### `calib` submodule pertains to the geometry calibration routine
33
+
34
+ ### `theory` submodule pertains to stored ab initio patterns, iam, or geometries.
35
+ ### `utils` has a bunch of useful functions in it
36
+ ### `xrayscatteringtools.io` has anything pertaning to loading or writing data (`combineRuns` is in here)
37
+ ### `plotting` has anything to do with plotting.
38
+
32
39
  ### List of all methods:
33
- All methods have full docstrings in the NumPy docstring standard.
34
- Proper namespaces have yet to be defined. Some of these functions can remain internal, and the more useful ones can be defined at the surface level.
40
+ All methods should have full docstrings in the NumPy docstring standard.
35
41
  * calib
36
42
  * geometry_calibration
37
43
  - run_geometry_calibration()
38
44
  - model()
39
45
  - thompson_correction()
40
46
  - geometry_correction()
47
+ - geometry_correction_units()
41
48
  * scattering_corrections
42
49
  - correction_factor()
43
50
  - Si_correction()
@@ -49,6 +56,7 @@ Proper namespaces have yet to be defined. Some of these functions can remain int
49
56
  - Al_attenuation_length()
50
57
  - Be_attenuation_length()
51
58
  - KaptonHN_attenuation_length()
59
+ - Zn_attenuation_length()
52
60
  - J4M_efficiency()
53
61
  * theory
54
62
  * iam
@@ -56,24 +64,40 @@ Proper namespaces have yet to be defined. Some of these functions can remain int
56
64
  - iam_inelastic_pattern()
57
65
  - iam_total_pattern()
58
66
  - iam_compton_spectrum()
67
+ - iam_elastic_pattern_oriented()
68
+ - iam_inelastic_pattern_oriented()
69
+ - iam_total_pattern_oriented()
59
70
  * patterns
60
- - sf6()
71
+ - SF6__CCSD__aug_cc_pVDZ
72
+ - SF6__HF__aug_cc_pVDZ
73
+ - SF6__MP2__aug_cc_pVDZ
74
+ * geometries
75
+ - SF6__CCSD_T_DHK__aug_cc_pV5Z_DK
61
76
  * io
62
77
  - combineRuns()
63
78
  - get_tree()
64
79
  - is_leaf()
65
80
  - get_leaves()
81
+ - get_data_paths()
66
82
  - runNumToString()
67
83
  - read_xyz()
84
+ - write_xyz()
85
+ - read_mol()
68
86
  * plotting
69
87
  - plot_jungfrau()
70
88
  - compute_pixel_edges()
71
89
  * utils
72
90
  - enable_underscore_cleanup()
73
91
  - azimuthalBinning()
92
+ - au2invAngstroms()
93
+ - invAngstroms2au()
74
94
  - keV2Angstroms()
75
95
  - Angstroms2keV()
76
96
  - q2theta()
77
97
  - theta2q()
78
98
  - element_symbol_to_number()
79
99
  - element_number_to_symbol()
100
+ - translate_molecule()
101
+ - rotate_molecule()
102
+ - J4M()
103
+ -
@@ -2,15 +2,22 @@
2
2
 
3
3
  ## A python library for the analysis of data from the CXI endstation at the LCLS.
4
4
 
5
+ ### `calib` submodule pertains to the geometry calibration routine
6
+
7
+ ### `theory` submodule pertains to stored ab initio patterns, iam, or geometries.
8
+ ### `utils` has a bunch of useful functions in it
9
+ ### `xrayscatteringtools.io` has anything pertaning to loading or writing data (`combineRuns` is in here)
10
+ ### `plotting` has anything to do with plotting.
11
+
5
12
  ### List of all methods:
6
- All methods have full docstrings in the NumPy docstring standard.
7
- Proper namespaces have yet to be defined. Some of these functions can remain internal, and the more useful ones can be defined at the surface level.
13
+ All methods should have full docstrings in the NumPy docstring standard.
8
14
  * calib
9
15
  * geometry_calibration
10
16
  - run_geometry_calibration()
11
17
  - model()
12
18
  - thompson_correction()
13
19
  - geometry_correction()
20
+ - geometry_correction_units()
14
21
  * scattering_corrections
15
22
  - correction_factor()
16
23
  - Si_correction()
@@ -22,6 +29,7 @@ Proper namespaces have yet to be defined. Some of these functions can remain int
22
29
  - Al_attenuation_length()
23
30
  - Be_attenuation_length()
24
31
  - KaptonHN_attenuation_length()
32
+ - Zn_attenuation_length()
25
33
  - J4M_efficiency()
26
34
  * theory
27
35
  * iam
@@ -29,24 +37,40 @@ Proper namespaces have yet to be defined. Some of these functions can remain int
29
37
  - iam_inelastic_pattern()
30
38
  - iam_total_pattern()
31
39
  - iam_compton_spectrum()
40
+ - iam_elastic_pattern_oriented()
41
+ - iam_inelastic_pattern_oriented()
42
+ - iam_total_pattern_oriented()
32
43
  * patterns
33
- - sf6()
44
+ - SF6__CCSD__aug_cc_pVDZ
45
+ - SF6__HF__aug_cc_pVDZ
46
+ - SF6__MP2__aug_cc_pVDZ
47
+ * geometries
48
+ - SF6__CCSD_T_DHK__aug_cc_pV5Z_DK
34
49
  * io
35
50
  - combineRuns()
36
51
  - get_tree()
37
52
  - is_leaf()
38
53
  - get_leaves()
54
+ - get_data_paths()
39
55
  - runNumToString()
40
56
  - read_xyz()
57
+ - write_xyz()
58
+ - read_mol()
41
59
  * plotting
42
60
  - plot_jungfrau()
43
61
  - compute_pixel_edges()
44
62
  * utils
45
63
  - enable_underscore_cleanup()
46
64
  - azimuthalBinning()
65
+ - au2invAngstroms()
66
+ - invAngstroms2au()
47
67
  - keV2Angstroms()
48
68
  - Angstroms2keV()
49
69
  - q2theta()
50
70
  - theta2q()
51
71
  - element_symbol_to_number()
52
72
  - element_number_to_symbol()
73
+ - translate_molecule()
74
+ - rotate_molecule()
75
+ - J4M()
76
+ -
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "xrayscatteringtools"
7
- version = "0.1.1"
7
+ version = "0.2.1"
8
8
  description = "A library for gas phase analysis at the CXI endstation"
9
9
  authors = [
10
10
  { name = "David Romano", email = "david_j_romano@brown.edu" }
@@ -0,0 +1,5 @@
1
+ from .io import combineRuns, get_leaves, read_xyz, write_xyz, read_mol, get_data_paths
2
+ from .plotting import plot_jungfrau, compute_pixel_edges
3
+ from .utils import enable_underscore_cleanup, azimuthalBinning, au2invAngstroms, invAngstroms2au, keV2Angstroms, Angstroms2keV, q2theta, theta2q, element_number_to_symbol, element_symbol_to_number, translate_molecule, rotate_molecule, J4M
4
+ from . import theory
5
+ from . import calib
@@ -0,0 +1,2 @@
1
+ from .geometry_calibration import run_geometry_calibration, model
2
+ from .scattering_corrections import correction_factor, J4M_efficiency
@@ -1,8 +1,8 @@
1
1
  import numpy as np
2
2
  from scipy.interpolate import InterpolatedUnivariateSpline
3
3
  from scipy.optimize import curve_fit
4
- from xrayscatteringtools.utils import theta2q
5
- from xrayscatteringtools.calib.scattering_corrections import correction_factor
4
+ from ..utils import theta2q
5
+ from .scattering_corrections import correction_factor
6
6
 
7
7
  def run_geometry_calibration(
8
8
  raw_image,
@@ -100,7 +100,22 @@ def run_geometry_calibration(
100
100
  fit = fitting_function([np.ravel(x), np.ravel(y)], *popt).reshape(raw_image.shape)
101
101
  return fit, popt, pcov
102
102
 
103
- def model(xy, amplitude, x0, y0, z0, phi0, photon_energy_keV, theory_interpolation):
103
+ def model(
104
+ xy,
105
+ amplitude,
106
+ x0,
107
+ y0,
108
+ z0,
109
+ phi0,
110
+ photon_energy_keV,
111
+ theory_interpolation,
112
+ do_geometry_correction=True,
113
+ do_thompson_correction=True,
114
+ do_angle_of_scattering_correction=True,
115
+ do_geometry_correction_units=False,
116
+ dx=75,
117
+ dy=75
118
+ ):
104
119
  """
105
120
  Calculate the theoretical detector image for given geometry parameters.
106
121
 
@@ -118,12 +133,25 @@ def model(xy, amplitude, x0, y0, z0, phi0, photon_energy_keV, theory_interpolati
118
133
  Photon energy in keV.
119
134
  theory_interpolation : callable
120
135
  Interpolated function for theoretical scattering intensities versus q.
136
+ do_geometry_correction : bool, optional
137
+ If True, apply geometry correction. Default is True.
138
+ do_thompson_correction : bool, optional
139
+ If True, apply Thompson polarization correction. Default is True.
140
+ do_angle_of_scattering_correction : bool, optional
141
+ If True, apply angle-of-scattering correction. Default is True.
142
+ do_geometry_correction_units : bool, optional
143
+ If True, apply geometry accounting for proper solid angle subtension. Default is False.
144
+ dx, dy : float, optional
145
+ Pixel size in microns for geometry correction with units. Default is 75 microns. If do_geometry_correction_units is False, these are ignored.
121
146
 
122
147
  Returns
123
148
  -------
124
149
  fit : ndarray
125
150
  Flattened array of predicted intensities for each pixel.
126
151
 
152
+ Notes
153
+ -----
154
+ Do not use both do_geometry_correction and do_geometry_correction_units at the same time.
127
155
  """
128
156
  # Pull out the x and y arrays from the input
129
157
  x = xy[0]
@@ -138,9 +166,14 @@ def model(xy, amplitude, x0, y0, z0, phi0, photon_energy_keV, theory_interpolati
138
166
 
139
167
  # Calculate corrections
140
168
  corrections = np.ones_like(q_matrix)
141
- corrections *= thompson_correction(centered_x, centered_y, z0, phi0) # Polarization
142
- corrections *= geometry_correction(centered_x, centered_y, z0) # Geometry
143
- corrections *= correction_factor(q_matrix, photon_energy_keV) # Angle-Of-Scattering, Lingyu Ma et al 2024 J. Phys. B: At. Mol. Opt. Phys. 57 205602
169
+ if do_thompson_correction:
170
+ corrections *= thompson_correction(centered_x, centered_y, z0, phi0) # Polarization
171
+ if do_geometry_correction:
172
+ corrections *= geometry_correction(centered_x, centered_y, z0) # Geometry
173
+ if do_angle_of_scattering_correction:
174
+ corrections *= correction_factor(q_matrix, photon_energy_keV) # Angle-Of-Scattering, Lingyu Ma et al 2024 J. Phys. B: At. Mol. Opt. Phys. 57 205602
175
+ if do_geometry_correction_units:
176
+ corrections *= geometry_correction_units(centered_x, centered_y, z0, dx, dy) # Geometry correctly accounting for solid angle subtension per pixel
144
177
  fit = amplitude * theory_interpolation(q_matrix) * corrections
145
178
  return fit
146
179
 
@@ -151,7 +184,7 @@ def thompson_correction(x, y, z0, phi0):
151
184
  Parameters
152
185
  ----------
153
186
  x, y : ndarray
154
- Pixel coordinates relative to detector origin.
187
+ Pixel coordinates relative to signal origin.
155
188
  z0 : float
156
189
  Detector distance along beam axis.
157
190
  phi0 : float
@@ -181,7 +214,7 @@ def geometry_correction(x, y, z0):
181
214
  Parameters
182
215
  ----------
183
216
  x, y : ndarray
184
- Pixel coordinates relative to detector origin.
217
+ Pixel coordinates relative to signal origin.
185
218
  z0 : float
186
219
  Detector distance along beam axis.
187
220
 
@@ -201,4 +234,32 @@ def geometry_correction(x, y, z0):
201
234
  r_matrix = np.sqrt(x**2 + y**2)
202
235
  theta = np.arctan(r_matrix / z0)
203
236
  correction = np.cos(theta) ** 3
237
+ return correction
238
+
239
+ def geometry_correction_units(x, y, z0, dx, dy):
240
+ """
241
+ Compute geometric correction factor z0^2 cos^3(theta) / dxdy for a detector, accounting for pixel area and distance units.
242
+
243
+ Parameters
244
+ ----------
245
+ x, y : ndarray
246
+ Pixel coordinates relative to signal origin.
247
+ z0 : float
248
+ Detector distance along beam axis.
249
+ dx, dy : float
250
+ Pixel size in x and y directions.
251
+
252
+ Returns
253
+ -------
254
+ correction : ndarray
255
+ Geometric correction factor for each pixel.
256
+
257
+ Notes
258
+ -----
259
+ The geometry correction comes from the inverse square law and the effective area of the pixel.
260
+ The inverse square law accounts for z^2 cos^2(theta), and the effective area is cos(theta)/dxdy.
261
+ """
262
+ r_matrix = np.sqrt(x**2 + y**2)
263
+ theta = np.arctan(r_matrix / z0)
264
+ correction = np.cos(theta)**3 * (dx * dy) / z0**2
204
265
  return correction
@@ -1,5 +1,5 @@
1
1
  from scipy.interpolate import InterpolatedUnivariateSpline
2
- from xrayscatteringtools.utils import q2theta
2
+ from ..utils import q2theta
3
3
  import numpy as np
4
4
  import pathlib
5
5
  _data_path = pathlib.Path(__file__).parent / "data"
@@ -481,7 +481,7 @@ def Zn_attenuation_length(keV):
481
481
  Uses spline interpolation of tabulated attenuation lengths (in microns),
482
482
  then converts them to meters.
483
483
  """
484
- with h5py.File('Zn_attenuation_length.h5', 'r') as f:
484
+ with h5py.File(f"{_data_path}/Zn_attenuation_length.h5", 'r') as f:
485
485
  E_values = f['E_values'][:]
486
486
  length = f['length'][:]
487
487
  Mu_Spline = InterpolatedUnivariateSpline(E_values, length)
@@ -1,10 +1,9 @@
1
1
  import numpy as np
2
2
  import h5py
3
- from tqdm.auto import tqdm
4
- from xrayscatteringtools.epicsArch import EpicsArchive
3
+ from .epicsArch import EpicsArchive
5
4
  from scipy.interpolate import interp1d
6
5
  import yaml
7
- from xrayscatteringtools.utils import element_number_to_symbol
6
+ from .utils import element_number_to_symbol
8
7
 
9
8
  def combineRuns(runNumbers, folders, keys_to_combine, keys_to_sum, keys_to_check, verbose=False, archImport=False):
10
9
  """
@@ -55,6 +54,7 @@ def combineRuns(runNumbers, folders, keys_to_combine, keys_to_sum, keys_to_check
55
54
  If multiple folders are provided but the number of folders does not match
56
55
  the number of run numbers.
57
56
  """
57
+ from tqdm.auto import tqdm # Lazy
58
58
  # Ensure runNumbers is a list
59
59
  if not isinstance(runNumbers, (list, tuple)):
60
60
  runNumbers = [runNumbers]
@@ -172,7 +172,7 @@ def is_leaf(dataset):
172
172
  """
173
173
  return isinstance(dataset, h5py.Dataset)
174
174
 
175
- def get_leaves(f, saveto, verbose=False):
175
+ def get_leaves(f, saveto=None, verbose=False):
176
176
  """Retrieve all leaf datasets from an HDF5 file and save them to a dictionary.
177
177
 
178
178
  Parameters
@@ -193,7 +193,8 @@ def get_leaves(f, saveto, verbose=False):
193
193
  if is_leaf(f[name]):
194
194
  if verbose:
195
195
  print(name, f[name][()].shape)
196
- saveto[name] = f[name][()]
196
+ if saveto is not None:
197
+ saveto[name] = f[name][()]
197
198
  f.visit(return_leaf)
198
199
 
199
200
  def get_data_paths(run_numbers, config_path='config.yaml'):
@@ -332,4 +333,111 @@ def write_xyz(filename, comment, atoms, coords):
332
333
  f.write(f"{len(atoms)}\n")
333
334
  f.write(f"{comment}\n")
334
335
  for atom, (x, y, z) in zip(atoms, coords):
335
- f.write(f"{atom} {x:.6f} {y:.6f} {z:.6f}\n")
336
+ f.write(f"{atom} {x:.6f} {y:.6f} {z:.6f}\n")
337
+
338
+ def read_mol(filename):
339
+ """
340
+ Read a molecular structure from a MOL file (MDL Molfile format).
341
+
342
+ Parameters
343
+ ----------
344
+ filename : str
345
+ Path to the MOL file to be read.
346
+
347
+ Returns
348
+ -------
349
+ molecule_name : str
350
+ Name of the molecule (first line of the file).
351
+ program_info : str
352
+ Program/user information (second line).
353
+ comment : str
354
+ Comment line (third line).
355
+ num_atoms : int
356
+ Number of atoms in the molecule.
357
+ num_bonds : int
358
+ Number of bonds in the molecule.
359
+ atoms : list of str
360
+ List of atomic symbols for each atom.
361
+ coords : list of tuple of float
362
+ Cartesian coordinates of each atom as (x, y, z) in Angstroms.
363
+ bonds : list of tuple of int
364
+ List of bonds as (atom1_index, atom2_index, bond_type).
365
+ Atom indices are 1-based as in the MOL file.
366
+ Bond types: 1=single, 2=double, 3=triple, 4=aromatic.
367
+ properties : dict
368
+ Additional properties from the atom and bond blocks.
369
+ - 'atom_properties': list of dicts with mass_diff, charge, stereo_parity, etc.
370
+ - 'bond_properties': list of dicts with stereo, topology, etc.
371
+
372
+ Notes
373
+ -----
374
+ The MOL file format (V2000) is expected to have:
375
+ 1. Line 1: Molecule name
376
+ 2. Line 2: Program/user info and timestamp
377
+ 3. Line 3: Comment
378
+ 4. Line 4: Counts line (atoms, bonds, etc.)
379
+ 5. Atom block: one line per atom with coordinates and properties
380
+ 6. Bond block: one line per bond
381
+ 7. Properties block (optional)
382
+ """
383
+ with open(filename, 'r') as f:
384
+ lines = f.readlines()
385
+
386
+ # Header block (first 3 lines)
387
+ molecule_name = lines[0].strip()
388
+ program_info = lines[1].strip()
389
+ comment = lines[2].strip()
390
+
391
+ # Counts line (line 4)
392
+ counts_line = lines[3]
393
+ num_atoms = int(counts_line[0:3].strip())
394
+ num_bonds = int(counts_line[3:6].strip())
395
+
396
+ # Atom block
397
+ atoms = []
398
+ coords = []
399
+ atom_properties = []
400
+
401
+ for i in range(4, 4 + num_atoms):
402
+ line = lines[i]
403
+ x = float(line[0:10].strip())
404
+ y = float(line[10:20].strip())
405
+ z = float(line[20:30].strip())
406
+ element = line[31:34].strip()
407
+
408
+ # Additional atom properties
409
+ mass_diff = int(line[34:36].strip()) if len(line) > 34 and line[34:36].strip() else 0
410
+ charge = int(line[36:39].strip()) if len(line) > 36 and line[36:39].strip() else 0
411
+
412
+ atoms.append(element)
413
+ coords.append((x, y, z))
414
+ atom_properties.append({
415
+ 'mass_diff': mass_diff,
416
+ 'charge': charge
417
+ })
418
+
419
+ # Bond block
420
+ bonds = []
421
+ bond_properties = []
422
+
423
+ bond_start = 4 + num_atoms
424
+ for i in range(bond_start, bond_start + num_bonds):
425
+ line = lines[i]
426
+ atom1 = int(line[0:3].strip())
427
+ atom2 = int(line[3:6].strip())
428
+ bond_type = int(line[6:9].strip())
429
+
430
+ # Additional bond properties
431
+ stereo = int(line[9:12].strip()) if len(line) > 9 and line[9:12].strip() else 0
432
+
433
+ bonds.append((atom1, atom2, bond_type))
434
+ bond_properties.append({
435
+ 'stereo': stereo
436
+ })
437
+
438
+ properties = {
439
+ 'atom_properties': atom_properties,
440
+ 'bond_properties': bond_properties
441
+ }
442
+
443
+ return molecule_name, program_info, comment, num_atoms, num_bonds, atoms, coords, bonds, properties
@@ -0,0 +1,7 @@
1
+ from .iam import iam_total_pattern, iam_elastic_pattern, iam_inelastic_pattern
2
+ from .iam import iam_total_pattern_oriented, iam_elastic_pattern_oriented, iam_inelastic_pattern_oriented
3
+ from .iam import iam_compton_spectrum
4
+
5
+ from .geometries import SF6__CCSD_T_DHK__aug_cc_pV5Z_DK
6
+
7
+ from .patterns import SF6__CCSD__aug_cc_pVDZ, SF6__MP2__aug_cc_pVDZ, SF6__HF__aug_cc_pVDZ
@@ -0,0 +1,38 @@
1
+ import h5py
2
+ import numpy as np
3
+
4
+ # Naming convention "Molecule__Method__Basis__Extras"
5
+ filename = "SF6__CCSD_T_DHK__aug_cc_pV5Z_DK"
6
+ molecule = "SF6"
7
+ method = "CCSD(T)+DHK"
8
+ basis_set = "aug-cc-pV5Z-DK"
9
+ n_electrons = 70
10
+ energy = -998.01159357
11
+ notes = "+DKH means that scalar-relativistic corrections via a tenth-order Douglas-Kroll-Hess Hamiltonian are included. This is also reflected by the -DK in the basis set. Mats Simmermacher sent over on Nov 17, 2025."
12
+ atoms = ['S', 'F', 'F', 'F', 'F', 'F', 'F']
13
+ charge = 0
14
+ geometry = np.array([
15
+ [ 0.0000000000, 0.0000000000, 0.0000000000],
16
+ [ 0.0000000000, 0.0000000000, 1.5600310790],
17
+ [ 0.0000000000, 0.0000000000, -1.5600310790],
18
+ [ 0.0000000000, 1.5600310790, 0.0000000000],
19
+ [ 0.0000000000, -1.5600310790, 0.0000000000],
20
+ [ 1.5600310790, 0.0000000000, 0.0000000000],
21
+ [-1.5600310790, 0.0000000000, 0.0000000000]
22
+ ])
23
+
24
+ # Creating the file and saving it
25
+ with h5py.File(filename + ".h5", "w") as f:
26
+
27
+ # Attributes
28
+ f.attrs["molecule"] = molecule
29
+ f.attrs["method"] = method
30
+ f.attrs["basis_set"] = basis_set
31
+ f.attrs["n_electrons"] = n_electrons
32
+ f.attrs["energy"] = energy
33
+ f.attrs["notes"] = notes
34
+ f.attrs["charge"] = charge
35
+ f.attrs["atoms"] = atoms
36
+
37
+ # Datasets
38
+ f.create_dataset("geometry", data=geometry)
@@ -17,7 +17,7 @@ molecule = "SF6"
17
17
  method = "HF"
18
18
  basis_set = "aug_cc_pVDZ"
19
19
  ##################### End User Set Parameters #####################
20
- h5_filename = f"{molecule}_{method}_{basis_set}.h5"
20
+ h5_filename = f"{molecule}__{method}__{basis_set}.h5"
21
21
 
22
22
  # Current conversion from a.u. to 1/Angstrom
23
23
  conversion_factor = au2invAngstroms(1.0)
@@ -0,0 +1,92 @@
1
+ from types import SimpleNamespace
2
+ import h5py
3
+ import pathlib
4
+
5
+ # Generally the import names go like 'molecule_method_basisset' Put all available names in __all__. These should match the .h5 files in the data directory.
6
+ __all__ = [
7
+ 'SF6__CCSD_T_DHK__aug_cc_pV5Z_DK'
8
+ ]
9
+ def __dir__():
10
+ # Tab completion for IPython
11
+ return sorted(__all__)
12
+
13
+
14
+ _data_path = pathlib.Path(__file__).parent / "data/geometries"
15
+
16
+ def __getattr__(name):
17
+ if name in __all__:
18
+ # Load from HDF5 only when first accessed
19
+ with h5py.File(f"{_data_path}/{name}.h5", "r") as f:
20
+ obj = _make_default_obj(f)
21
+ obj.__doc__ = _make_default_docstring(obj)
22
+ return obj
23
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
24
+
25
+ def _make_default_obj(f):
26
+ """
27
+ Create a SimpleNamespace object with default attributes from an HDF5 file.
28
+
29
+ Parameters
30
+ ----------
31
+ f : h5py.File
32
+ An open HDF5 file object containing the datasets and attributes.
33
+
34
+ Returns
35
+ -------
36
+ obj : SimpleNamespace
37
+ A SimpleNamespace object with attributes: geometry, molecule, method, basis_set, n_electrons, charge, energy, atoms, notes.
38
+ """
39
+ return SimpleNamespace(
40
+ geometry=f["geometry"][:],
41
+ molecule=f.attrs["molecule"],
42
+ method=f.attrs["method"],
43
+ basis_set=f.attrs["basis_set"],
44
+ n_electrons=f.attrs["n_electrons"],
45
+ charge=f.attrs["charge"],
46
+ energy=f.attrs["energy"],
47
+ atoms=f.attrs["atoms"],
48
+ notes=f.attrs["notes"]
49
+ )
50
+
51
+ def _make_default_docstring(obj):
52
+ """
53
+ Generate a default docstring for a geometry object.
54
+
55
+ Parameters
56
+ ----------
57
+ obj : SimpleNamespace
58
+ The data object with attributes: geometry, molecule, method, basis_set, n_electrons.
59
+
60
+ Returns
61
+ -------
62
+ doc : str
63
+ The generated docstring.
64
+ """
65
+
66
+ doc = f"""
67
+ Optimized geometry for {obj.molecule} at the {obj.method}/{obj.basis_set} level of theory.
68
+
69
+ Attributes
70
+ ----------
71
+ geometry : Nx3 numpy array
72
+ The geometry data of the molecule, in Angstroms.
73
+ atoms : list of str
74
+ The list of atomic symbols corresponding to the geometry.
75
+ molecule : str
76
+ The molecular formula of the species.
77
+ method : str
78
+ The method used for the calculation.
79
+ basis_set : str
80
+ The basis set used for the calculation.
81
+ n_electrons : int
82
+ The number of electrons in the molecule.
83
+ charge : int
84
+ The net charge of the molecule.
85
+ energy : float
86
+ The total electronic energy of the molecule in Hartree.
87
+
88
+ Notes
89
+ -----
90
+ {obj.notes}
91
+ """
92
+ return doc
@@ -0,0 +1,17 @@
1
+ from types import SimpleNamespace
2
+
3
+ __all__: list[str]
4
+
5
+ class _GeometryData(SimpleNamespace):
6
+ """Type stub for ab initio geometry data."""
7
+ geometry: any # ndarray
8
+ molecule: str
9
+ method: str
10
+ basis_set: str
11
+ n_electrons: int
12
+ charge: int
13
+ energy: float
14
+ atoms: list[str]
15
+ notes: str
16
+
17
+ SF6__CCSD_T_DHK__aug_cc_pV5Z_DK: _GeometryData