advisor-scattering 0.5.0__py3-none-any.whl

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 (69) hide show
  1. advisor/__init__.py +3 -0
  2. advisor/__main__.py +7 -0
  3. advisor/app.py +40 -0
  4. advisor/controllers/__init__.py +6 -0
  5. advisor/controllers/app_controller.py +69 -0
  6. advisor/controllers/feature_controller.py +25 -0
  7. advisor/domain/__init__.py +23 -0
  8. advisor/domain/core/__init__.py +8 -0
  9. advisor/domain/core/lab.py +121 -0
  10. advisor/domain/core/lattice.py +79 -0
  11. advisor/domain/core/sample.py +101 -0
  12. advisor/domain/geometry.py +212 -0
  13. advisor/domain/unit_converter.py +82 -0
  14. advisor/features/__init__.py +6 -0
  15. advisor/features/scattering_geometry/controllers/__init__.py +5 -0
  16. advisor/features/scattering_geometry/controllers/scattering_geometry_controller.py +26 -0
  17. advisor/features/scattering_geometry/domain/__init__.py +5 -0
  18. advisor/features/scattering_geometry/domain/brillouin_calculator.py +410 -0
  19. advisor/features/scattering_geometry/domain/core.py +516 -0
  20. advisor/features/scattering_geometry/ui/__init__.py +5 -0
  21. advisor/features/scattering_geometry/ui/components/__init__.py +17 -0
  22. advisor/features/scattering_geometry/ui/components/angles_to_hkl_components.py +150 -0
  23. advisor/features/scattering_geometry/ui/components/hk_angles_components.py +430 -0
  24. advisor/features/scattering_geometry/ui/components/hkl_scan_components.py +526 -0
  25. advisor/features/scattering_geometry/ui/components/hkl_to_angles_components.py +315 -0
  26. advisor/features/scattering_geometry/ui/scattering_geometry_tab.py +725 -0
  27. advisor/features/structure_factor/controllers/__init__.py +6 -0
  28. advisor/features/structure_factor/controllers/structure_factor_controller.py +25 -0
  29. advisor/features/structure_factor/domain/__init__.py +6 -0
  30. advisor/features/structure_factor/domain/structure_factor_calculator.py +107 -0
  31. advisor/features/structure_factor/ui/__init__.py +6 -0
  32. advisor/features/structure_factor/ui/components/__init__.py +12 -0
  33. advisor/features/structure_factor/ui/components/customized_plane_components.py +358 -0
  34. advisor/features/structure_factor/ui/components/hkl_plane_components.py +391 -0
  35. advisor/features/structure_factor/ui/structure_factor_tab.py +273 -0
  36. advisor/resources/__init__.py +0 -0
  37. advisor/resources/config/app_config.json +14 -0
  38. advisor/resources/config/tips.json +4 -0
  39. advisor/resources/data/nacl.cif +111 -0
  40. advisor/resources/icons/bz_caculator.jpg +0 -0
  41. advisor/resources/icons/bz_calculator.png +0 -0
  42. advisor/resources/icons/minus.svg +3 -0
  43. advisor/resources/icons/placeholder.png +0 -0
  44. advisor/resources/icons/plus.svg +3 -0
  45. advisor/resources/icons/reset.png +0 -0
  46. advisor/resources/icons/sf_calculator.jpg +0 -0
  47. advisor/resources/icons/sf_calculator.png +0 -0
  48. advisor/resources/icons.qrc +6 -0
  49. advisor/resources/qss/styles.qss +348 -0
  50. advisor/resources/resources_rc.py +83 -0
  51. advisor/ui/__init__.py +7 -0
  52. advisor/ui/init_window.py +566 -0
  53. advisor/ui/main_window.py +174 -0
  54. advisor/ui/tab_interface.py +44 -0
  55. advisor/ui/tips.py +30 -0
  56. advisor/ui/utils/__init__.py +6 -0
  57. advisor/ui/utils/readcif.py +129 -0
  58. advisor/ui/visualizers/HKLScan2DVisualizer.py +224 -0
  59. advisor/ui/visualizers/__init__.py +8 -0
  60. advisor/ui/visualizers/coordinate_visualizer.py +203 -0
  61. advisor/ui/visualizers/scattering_visualizer.py +301 -0
  62. advisor/ui/visualizers/structure_factor_visualizer.py +426 -0
  63. advisor/ui/visualizers/structure_factor_visualizer_2d.py +235 -0
  64. advisor/ui/visualizers/unitcell_visualizer.py +518 -0
  65. advisor_scattering-0.5.0.dist-info/METADATA +122 -0
  66. advisor_scattering-0.5.0.dist-info/RECORD +69 -0
  67. advisor_scattering-0.5.0.dist-info/WHEEL +5 -0
  68. advisor_scattering-0.5.0.dist-info/entry_points.txt +3 -0
  69. advisor_scattering-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,212 @@
1
+ """
2
+ here includes the translation functions
3
+ """
4
+
5
+ import numpy as np
6
+
7
+
8
+ def get_real_space_vectors(a, b, c, alpha, beta, gamma):
9
+ """Get the real space vectors a_vec, b_vec, c_vec from the lattice parameters.
10
+ - a_vec is by-default along x-axis (a, 0, 0)
11
+ - b_vec is by-default (b cos gamma, b sin gamma, 0) on the x-y plane,
12
+ - c_vec is then calculated
13
+ The above convention defines the lattice coordinate system.
14
+
15
+ Args:
16
+ a, b, c (float): Lattice constants in Angstroms
17
+ alpha, beta, gamma (float): Lattice angles in degrees
18
+
19
+ Returns:
20
+ a_vec, b_vec, c_vec (np.ndarray): Real space vectors
21
+ """
22
+ alpha_rad, beta_rad, gamma_rad = (
23
+ np.radians(alpha),
24
+ np.radians(beta),
25
+ np.radians(gamma),
26
+ )
27
+ a_vec = np.array([a, 0, 0])
28
+ b_vec = np.array([b * np.cos(gamma_rad), b * np.sin(gamma_rad), 0])
29
+ c_vec_x = c * np.cos(beta_rad)
30
+ c_vec_y = (
31
+ c
32
+ * (np.cos(alpha_rad) - np.cos(beta_rad) * np.cos(gamma_rad))
33
+ / np.sin(gamma_rad)
34
+ )
35
+ c_vec_z = np.sqrt(c**2 - c_vec_x**2 - c_vec_y**2)
36
+ c_vec = np.array([c_vec_x, c_vec_y, c_vec_z])
37
+ return a_vec, b_vec, c_vec
38
+
39
+
40
+ def get_reciprocal_space_vectors(a, b, c, alpha, beta, gamma):
41
+ """Get the reciprocal space vectors a_star_vec, b_star_vec, c_star_vec from the lattice
42
+ parameters, angles in degrees. These vectors are in the crystal coordinate system.
43
+ """
44
+ a_vec, b_vec, c_vec = get_real_space_vectors(a, b, c, alpha, beta, gamma)
45
+ volumn = abs(np.dot(a_vec, np.cross(b_vec, c_vec)))
46
+ a_star_vec = 2 * np.pi * np.cross(b_vec, c_vec) / volumn
47
+ b_star_vec = 2 * np.pi * np.cross(c_vec, a_vec) / volumn
48
+ c_star_vec = 2 * np.pi * np.cross(a_vec, b_vec) / volumn
49
+ return a_star_vec, b_star_vec, c_star_vec
50
+
51
+
52
+ def euler_to_matrix(roll, pitch, yaw):
53
+ """Convert Euler angles to rotation matrix. We follows the ZYX convention.
54
+ Remember we are using a right-hand rule.
55
+
56
+ Args:
57
+ roll (float): rotation about the new X axis in degrees
58
+ pitch (float): rotation about the new Y axis in degrees
59
+ yaw (float): rotation about the original z axis in degrees
60
+
61
+ Returns:
62
+ rotation_matrix (np.ndarray): Rotation matrix
63
+ """
64
+ roll_rad, pitch_rad, yaw_rad = (
65
+ np.radians(roll),
66
+ np.radians(pitch),
67
+ np.radians(yaw),
68
+ )
69
+ Rx = np.array(
70
+ [
71
+ [1, 0, 0],
72
+ [0, np.cos(roll_rad), -np.sin(roll_rad)],
73
+ [0, np.sin(roll_rad), np.cos(roll_rad)],
74
+ ]
75
+ )
76
+ # remember this is a right-hand rule
77
+ Ry = np.array(
78
+ [
79
+ [np.cos(pitch_rad), 0, np.sin(pitch_rad)],
80
+ [0, 1, 0],
81
+ [-np.sin(pitch_rad), 0, np.cos(pitch_rad)],
82
+ ]
83
+ )
84
+
85
+ Rz = np.array(
86
+ [
87
+ [np.cos(yaw_rad), -np.sin(yaw_rad), 0],
88
+ [np.sin(yaw_rad), np.cos(yaw_rad), 0],
89
+ [0, 0, 1],
90
+ ]
91
+ )
92
+
93
+ return Rz @ Ry @ Rx # ZYX order
94
+
95
+
96
+ def angle_to_matrix(theta, phi, chi, is_inverse = False):
97
+ """Convert angles theta, phi, chi to rotation matrix.
98
+ Pay attention to the direction of the rotation.
99
+
100
+ Updated for x-y scattering plane (z-axis is normal to scattering plane).
101
+
102
+ Args:
103
+ theta (float): rotation about the z-axis in degrees, right-hand rule
104
+ phi (float): rotation about the x-axis in degrees, right-hand rule
105
+ chi (float): rotation about the y-axis in degrees, right-hand rule
106
+
107
+ Returns:
108
+ rotation_matrix (np.ndarray): Rotation matrix
109
+ """
110
+ theta_rad, phi_rad, chi_rad = (np.radians(theta), np.radians(phi), np.radians(chi))
111
+
112
+ # theta rotation around the z-axis (perpendicular to scattering plane)
113
+ theta_mat = np.array(
114
+ [
115
+ [np.cos(theta_rad), -np.sin(theta_rad), 0],
116
+ [np.sin(theta_rad), np.cos(theta_rad), 0],
117
+ [0, 0, 1],
118
+ ]
119
+ )
120
+ # chi rotation around the y-axis (in scattering plane)
121
+ chi_mat = np.array(
122
+ [
123
+ [np.cos(chi_rad), 0, np.sin(chi_rad)],
124
+ [0, 1, 0],
125
+ [-np.sin(chi_rad), 0, np.cos(chi_rad)],
126
+ ]
127
+ )
128
+
129
+ # phi rotation around the x-axis (in scattering plane)
130
+ phi_mat = np.array(
131
+ [
132
+ [1, 0, 0],
133
+ [0, np.cos(phi_rad), -np.sin(phi_rad)],
134
+ [0, np.sin(phi_rad), np.cos(phi_rad)],
135
+ ]
136
+ )
137
+
138
+ matrix = theta_mat @ chi_mat @ phi_mat
139
+ if is_inverse:
140
+ matrix = matrix.T
141
+ return matrix
142
+
143
+
144
+ def get_rotation(phi, chi, is_inverse = False):
145
+ """get the rotational matrix that rotates the sample with respect to the scattering plane.
146
+
147
+ Updated for x-y scattering plane (z-axis is normal to scattering plane).
148
+ phi: rotation about x-axis (in scattering plane)
149
+ chi: rotation about y-axis (in scattering plane)
150
+ """
151
+ # Convert angles to radians
152
+ phi_rad = np.radians(phi)
153
+ chi_rad = np.radians(chi)
154
+
155
+ # chi rotation about y-axis (in scattering plane)
156
+ chi_mat_sample = np.array(
157
+ [
158
+ [np.cos(chi_rad), 0, np.sin(chi_rad)],
159
+ [0, 1, 0],
160
+ [-np.sin(chi_rad), 0, np.cos(chi_rad)],
161
+ ]
162
+ )
163
+ # phi rotation about x-axis (in scattering plane)
164
+ phi_mat_sample = np.array(
165
+ [
166
+ [1, 0, 0],
167
+ [0, np.cos(phi_rad), -np.sin(phi_rad)],
168
+ [0, np.sin(phi_rad), np.cos(phi_rad)],
169
+ ]
170
+ )
171
+ matrix = chi_mat_sample @ phi_mat_sample
172
+ if is_inverse:
173
+ matrix = matrix.T
174
+ return matrix
175
+
176
+
177
+
178
+
179
+ def sample_to_lab_conversion(
180
+ a_vec_sample, b_vec_sample, c_vec_sample, roll, pitch, yaw
181
+ ):
182
+ """Convert vectors from sample coordinate system to lab coordinate system.
183
+
184
+ Args:
185
+ a_vec_sample, b_vec_sample, c_vec_sample (np.ndarray): Vectors in sample coordinate system
186
+ roll, pitch, yaw (float): Euler angles in degrees
187
+
188
+ Returns:
189
+ a_vec_lab, b_vec_lab, c_vec_lab (np.ndarray): Vectors in lab coordinate system
190
+ """
191
+ rotation_matrix = euler_to_matrix(roll, pitch, yaw)
192
+ a_vec_lab = rotation_matrix @ a_vec_sample
193
+ b_vec_lab = rotation_matrix @ b_vec_sample
194
+ c_vec_lab = rotation_matrix @ c_vec_sample
195
+ return a_vec_lab, b_vec_lab, c_vec_lab
196
+
197
+
198
+ def lab_to_sample_conversion(a_vec_lab, b_vec_lab, c_vec_lab, roll, pitch, yaw):
199
+ """Convert vectors from lab coordinate system to sample coordinate system.
200
+
201
+ Args:
202
+ a_vec_lab, b_vec_lab, c_vec_lab (np.ndarray): Vectors in lab coordinate system
203
+ roll, pitch, yaw (float): Euler angles in degrees
204
+
205
+ Returns:
206
+ a_vec_sample, b_vec_sample, c_vec_sample (np.ndarray): Vectors in sample coordinate system
207
+ """
208
+ rotation_matrix = euler_to_matrix(roll, pitch, yaw)
209
+ a_vec_sample = rotation_matrix.T @ a_vec_lab
210
+ b_vec_sample = rotation_matrix.T @ b_vec_lab
211
+ c_vec_sample = rotation_matrix.T @ c_vec_lab
212
+ return a_vec_sample, b_vec_sample, c_vec_sample
@@ -0,0 +1,82 @@
1
+ import numpy as np
2
+ from typing import Union, Optional
3
+
4
+
5
+ class UnitConverter:
6
+ """
7
+ A class for converting between different units commonly used in X-ray spectroscopy.
8
+ """
9
+
10
+ def __init__(self):
11
+ # Fundamental constants
12
+ self.angstrom_to_ev_constant = 12398.425
13
+ self.ev_to_phz_constant = 0.2418
14
+ self.ev_to_momentum_constant = 0.080656 # in A^-1
15
+
16
+ def ev_to_angstrom(
17
+ self, energy: Union[float, np.ndarray]
18
+ ) -> Union[float, np.ndarray]:
19
+ """
20
+ Convert X-ray energy from eV to wavelength in Angstrom.
21
+
22
+ Args:
23
+ energy: Energy in eV
24
+
25
+ Returns:
26
+ Wavelength in Angstrom
27
+ """
28
+ return self.angstrom_to_ev_constant / energy
29
+
30
+ def angstrom_to_ev(
31
+ self, wavelength: Union[float, np.ndarray]
32
+ ) -> Union[float, np.ndarray]:
33
+ """
34
+ Convert X-ray wavelength from Angstrom to energy in eV.
35
+
36
+ Args:
37
+ wavelength: Wavelength in Angstrom
38
+
39
+ Returns:
40
+ Energy in eV
41
+ """
42
+ return self.angstrom_to_ev_constant / wavelength
43
+
44
+ def ev_to_phz(self, energy: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
45
+ """
46
+ Convert X-ray energy from eV to frequency in PHz.
47
+
48
+ Args:
49
+ energy: Energy in eV
50
+
51
+ Returns:
52
+ Frequency in PHz
53
+ """
54
+ return energy * self.ev_to_phz_constant
55
+
56
+ def phz_to_ev(
57
+ self, frequency: Union[float, np.ndarray]
58
+ ) -> Union[float, np.ndarray]:
59
+ """
60
+ Convert X-ray frequency from PHz to energy in eV.
61
+
62
+ Args:
63
+ frequency: Frequency in PHz
64
+
65
+ Returns:
66
+ Energy in eV
67
+ """
68
+ return frequency / self.ev_to_phz_constant
69
+
70
+ def ev_to_momentum(
71
+ self, energy: Union[float, np.ndarray]
72
+ ) -> Union[float, np.ndarray]:
73
+ """
74
+ Convert X-ray energy from eV to momentum in Å^-1.
75
+
76
+ Args:
77
+ energy: Energy in eV
78
+
79
+ Returns:
80
+ Momentum in Å^-1
81
+ """
82
+ return energy * self.ev_to_momentum_constant
@@ -0,0 +1,6 @@
1
+ """Feature packages."""
2
+
3
+ from advisor.features.scattering_geometry.controllers import ScatteringGeometryController
4
+ from advisor.features.structure_factor.controllers import StructureFactorController
5
+
6
+ __all__ = ["ScatteringGeometryController", "StructureFactorController"]
@@ -0,0 +1,5 @@
1
+ """Controllers for scattering geometry feature."""
2
+
3
+ from .scattering_geometry_controller import ScatteringGeometryController
4
+
5
+ __all__ = ["ScatteringGeometryController"]
@@ -0,0 +1,26 @@
1
+ """Controller for the scattering geometry (Brillouin) feature."""
2
+
3
+ from advisor.controllers.feature_controller import FeatureController
4
+ from advisor.features.scattering_geometry.domain import BrillouinCalculator
5
+ from advisor.features.scattering_geometry.ui.scattering_geometry_tab import ScatteringGeometryTab
6
+
7
+
8
+ class ScatteringGeometryController(FeatureController):
9
+ """Manages the Brillouin/scattering geometry feature."""
10
+
11
+ title = "Scattering Geometry"
12
+ description = "Convert angles ↔ HKL, scan HKL trajectories, and visualize scattering geometry."
13
+ icon = "bz_calculator.png"
14
+
15
+ def __init__(self, app_controller):
16
+ super().__init__(app_controller)
17
+ self.calculator = BrillouinCalculator()
18
+ self.view = self.build_view()
19
+
20
+ def build_view(self):
21
+ return ScatteringGeometryTab(controller=self, calculator=self.calculator)
22
+
23
+ def set_parameters(self, params: dict):
24
+ self.calculator.initialize(params=params)
25
+ if self.view and hasattr(self.view, "set_parameters"):
26
+ self.view.set_parameters(params)
@@ -0,0 +1,5 @@
1
+ """Domain logic for scattering geometry feature."""
2
+
3
+ from .brillouin_calculator import BrillouinCalculator
4
+
5
+ __all__ = ["BrillouinCalculator"]