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.
- advisor/__init__.py +3 -0
- advisor/__main__.py +7 -0
- advisor/app.py +40 -0
- advisor/controllers/__init__.py +6 -0
- advisor/controllers/app_controller.py +69 -0
- advisor/controllers/feature_controller.py +25 -0
- advisor/domain/__init__.py +23 -0
- advisor/domain/core/__init__.py +8 -0
- advisor/domain/core/lab.py +121 -0
- advisor/domain/core/lattice.py +79 -0
- advisor/domain/core/sample.py +101 -0
- advisor/domain/geometry.py +212 -0
- advisor/domain/unit_converter.py +82 -0
- advisor/features/__init__.py +6 -0
- advisor/features/scattering_geometry/controllers/__init__.py +5 -0
- advisor/features/scattering_geometry/controllers/scattering_geometry_controller.py +26 -0
- advisor/features/scattering_geometry/domain/__init__.py +5 -0
- advisor/features/scattering_geometry/domain/brillouin_calculator.py +410 -0
- advisor/features/scattering_geometry/domain/core.py +516 -0
- advisor/features/scattering_geometry/ui/__init__.py +5 -0
- advisor/features/scattering_geometry/ui/components/__init__.py +17 -0
- advisor/features/scattering_geometry/ui/components/angles_to_hkl_components.py +150 -0
- advisor/features/scattering_geometry/ui/components/hk_angles_components.py +430 -0
- advisor/features/scattering_geometry/ui/components/hkl_scan_components.py +526 -0
- advisor/features/scattering_geometry/ui/components/hkl_to_angles_components.py +315 -0
- advisor/features/scattering_geometry/ui/scattering_geometry_tab.py +725 -0
- advisor/features/structure_factor/controllers/__init__.py +6 -0
- advisor/features/structure_factor/controllers/structure_factor_controller.py +25 -0
- advisor/features/structure_factor/domain/__init__.py +6 -0
- advisor/features/structure_factor/domain/structure_factor_calculator.py +107 -0
- advisor/features/structure_factor/ui/__init__.py +6 -0
- advisor/features/structure_factor/ui/components/__init__.py +12 -0
- advisor/features/structure_factor/ui/components/customized_plane_components.py +358 -0
- advisor/features/structure_factor/ui/components/hkl_plane_components.py +391 -0
- advisor/features/structure_factor/ui/structure_factor_tab.py +273 -0
- advisor/resources/__init__.py +0 -0
- advisor/resources/config/app_config.json +14 -0
- advisor/resources/config/tips.json +4 -0
- advisor/resources/data/nacl.cif +111 -0
- advisor/resources/icons/bz_caculator.jpg +0 -0
- advisor/resources/icons/bz_calculator.png +0 -0
- advisor/resources/icons/minus.svg +3 -0
- advisor/resources/icons/placeholder.png +0 -0
- advisor/resources/icons/plus.svg +3 -0
- advisor/resources/icons/reset.png +0 -0
- advisor/resources/icons/sf_calculator.jpg +0 -0
- advisor/resources/icons/sf_calculator.png +0 -0
- advisor/resources/icons.qrc +6 -0
- advisor/resources/qss/styles.qss +348 -0
- advisor/resources/resources_rc.py +83 -0
- advisor/ui/__init__.py +7 -0
- advisor/ui/init_window.py +566 -0
- advisor/ui/main_window.py +174 -0
- advisor/ui/tab_interface.py +44 -0
- advisor/ui/tips.py +30 -0
- advisor/ui/utils/__init__.py +6 -0
- advisor/ui/utils/readcif.py +129 -0
- advisor/ui/visualizers/HKLScan2DVisualizer.py +224 -0
- advisor/ui/visualizers/__init__.py +8 -0
- advisor/ui/visualizers/coordinate_visualizer.py +203 -0
- advisor/ui/visualizers/scattering_visualizer.py +301 -0
- advisor/ui/visualizers/structure_factor_visualizer.py +426 -0
- advisor/ui/visualizers/structure_factor_visualizer_2d.py +235 -0
- advisor/ui/visualizers/unitcell_visualizer.py +518 -0
- advisor_scattering-0.5.0.dist-info/METADATA +122 -0
- advisor_scattering-0.5.0.dist-info/RECORD +69 -0
- advisor_scattering-0.5.0.dist-info/WHEEL +5 -0
- advisor_scattering-0.5.0.dist-info/entry_points.txt +3 -0
- 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,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)
|