advisor-scattering 0.5.0__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.
- advisor_scattering-0.5.0/MANIFEST.in +2 -0
- advisor_scattering-0.5.0/PKG-INFO +122 -0
- advisor_scattering-0.5.0/README.md +100 -0
- advisor_scattering-0.5.0/advisor/__init__.py +3 -0
- advisor_scattering-0.5.0/advisor/__main__.py +7 -0
- advisor_scattering-0.5.0/advisor/app.py +40 -0
- advisor_scattering-0.5.0/advisor/controllers/__init__.py +6 -0
- advisor_scattering-0.5.0/advisor/controllers/app_controller.py +69 -0
- advisor_scattering-0.5.0/advisor/controllers/feature_controller.py +25 -0
- advisor_scattering-0.5.0/advisor/domain/__init__.py +23 -0
- advisor_scattering-0.5.0/advisor/domain/core/__init__.py +8 -0
- advisor_scattering-0.5.0/advisor/domain/core/lab.py +121 -0
- advisor_scattering-0.5.0/advisor/domain/core/lattice.py +79 -0
- advisor_scattering-0.5.0/advisor/domain/core/sample.py +101 -0
- advisor_scattering-0.5.0/advisor/domain/geometry.py +212 -0
- advisor_scattering-0.5.0/advisor/domain/unit_converter.py +82 -0
- advisor_scattering-0.5.0/advisor/features/__init__.py +6 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/controllers/__init__.py +5 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/controllers/scattering_geometry_controller.py +26 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/domain/__init__.py +5 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/domain/brillouin_calculator.py +410 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/domain/core.py +516 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/__init__.py +5 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/__init__.py +17 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/angles_to_hkl_components.py +150 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/hk_angles_components.py +430 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/hkl_scan_components.py +526 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/hkl_to_angles_components.py +315 -0
- advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/scattering_geometry_tab.py +725 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/controllers/__init__.py +6 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/controllers/structure_factor_controller.py +25 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/domain/__init__.py +6 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/domain/structure_factor_calculator.py +107 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/ui/__init__.py +6 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/ui/components/__init__.py +12 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/ui/components/customized_plane_components.py +358 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/ui/components/hkl_plane_components.py +391 -0
- advisor_scattering-0.5.0/advisor/features/structure_factor/ui/structure_factor_tab.py +273 -0
- advisor_scattering-0.5.0/advisor/resources/__init__.py +0 -0
- advisor_scattering-0.5.0/advisor/resources/config/app_config.json +14 -0
- advisor_scattering-0.5.0/advisor/resources/config/tips.json +4 -0
- advisor_scattering-0.5.0/advisor/resources/data/nacl.cif +111 -0
- advisor_scattering-0.5.0/advisor/resources/icons/bz_caculator.jpg +0 -0
- advisor_scattering-0.5.0/advisor/resources/icons/bz_calculator.png +0 -0
- advisor_scattering-0.5.0/advisor/resources/icons/minus.svg +3 -0
- advisor_scattering-0.5.0/advisor/resources/icons/placeholder.png +0 -0
- advisor_scattering-0.5.0/advisor/resources/icons/plus.svg +3 -0
- advisor_scattering-0.5.0/advisor/resources/icons/reset.png +0 -0
- advisor_scattering-0.5.0/advisor/resources/icons/sf_calculator.jpg +0 -0
- advisor_scattering-0.5.0/advisor/resources/icons/sf_calculator.png +0 -0
- advisor_scattering-0.5.0/advisor/resources/icons.qrc +6 -0
- advisor_scattering-0.5.0/advisor/resources/qss/styles.qss +348 -0
- advisor_scattering-0.5.0/advisor/resources/resources_rc.py +83 -0
- advisor_scattering-0.5.0/advisor/ui/__init__.py +7 -0
- advisor_scattering-0.5.0/advisor/ui/init_window.py +566 -0
- advisor_scattering-0.5.0/advisor/ui/main_window.py +174 -0
- advisor_scattering-0.5.0/advisor/ui/tab_interface.py +44 -0
- advisor_scattering-0.5.0/advisor/ui/tips.py +30 -0
- advisor_scattering-0.5.0/advisor/ui/utils/__init__.py +6 -0
- advisor_scattering-0.5.0/advisor/ui/utils/readcif.py +129 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/HKLScan2DVisualizer.py +224 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/__init__.py +8 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/coordinate_visualizer.py +203 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/scattering_visualizer.py +301 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/structure_factor_visualizer.py +426 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/structure_factor_visualizer_2d.py +235 -0
- advisor_scattering-0.5.0/advisor/ui/visualizers/unitcell_visualizer.py +518 -0
- advisor_scattering-0.5.0/advisor_scattering.egg-info/PKG-INFO +122 -0
- advisor_scattering-0.5.0/advisor_scattering.egg-info/SOURCES.txt +73 -0
- advisor_scattering-0.5.0/advisor_scattering.egg-info/dependency_links.txt +1 -0
- advisor_scattering-0.5.0/advisor_scattering.egg-info/entry_points.txt +3 -0
- advisor_scattering-0.5.0/advisor_scattering.egg-info/requires.txt +5 -0
- advisor_scattering-0.5.0/advisor_scattering.egg-info/top_level.txt +1 -0
- advisor_scattering-0.5.0/pyproject.toml +47 -0
- advisor_scattering-0.5.0/setup.cfg +4 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: advisor-scattering
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Advisor-Scattering: Advanced Visual Scattering Toolkit for Reciprocal-space
|
|
5
|
+
Author: Xunyang Hong
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://github.com/HongXunyang/advisor
|
|
8
|
+
Project-URL: Documentation, https://advisor-scattering.readthedocs.io/en/latest/
|
|
9
|
+
Keywords: scattering,reciprocal-space,pyqt,visualization
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: License :: Other/Proprietary License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: PyQt5>=5.15.11
|
|
18
|
+
Requires-Dist: matplotlib>=3.9.2
|
|
19
|
+
Requires-Dist: numpy>=2.0.2
|
|
20
|
+
Requires-Dist: scipy>=1.13.1
|
|
21
|
+
Requires-Dist: Dans_Diffraction>=3.3.3
|
|
22
|
+
|
|
23
|
+
# Advisor-Scattering — Advanced Visual Scattering Toolkit for Reciprocal-space
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
Advisor-Scattering is a PyQt5 desktop app for X-ray scattering/diffraction experiments. It helps you convert scattering angles ↔ momentum transfer (HKL), explore scattering geometry, and visualize structure factors—all with interactive plots. Full docs on *[Read the Docs](https://advisor-scattering.readthedocs.io/en/latest/)*.
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
or use the link below to view the demo video.
|
|
32
|
+
▶ [Demo video (MP4)](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/showcase.mp4)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
- Convert scattering angles to momentum transfer (HKL) and vice versa.
|
|
37
|
+
- Visualize scattering geometry and unit cells
|
|
38
|
+
- Compute and visualize structure factors in reciprocal space.
|
|
39
|
+
- CIF file drop-in support
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
- Python 3.8+ with PyQt5, numpy, scipy, matplotlib, Dans_Diffraction (see `requirements.txt`).
|
|
44
|
+
|
|
45
|
+
From PyPI:
|
|
46
|
+
```bash
|
|
47
|
+
pip install advisor-scattering
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
From source:
|
|
51
|
+
```bash
|
|
52
|
+
python -m venv .venv
|
|
53
|
+
source .venv/bin/activate # .venv\Scripts\activate on Windows
|
|
54
|
+
pip install -r requirements.txt
|
|
55
|
+
pip install .
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Run
|
|
59
|
+
```bash
|
|
60
|
+
advisor-scattering
|
|
61
|
+
# or
|
|
62
|
+
advisor
|
|
63
|
+
# or
|
|
64
|
+
python -m advisor
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
*Note: the install command is `pip install advisor-scattering`, and the import is `import advisor`*.
|
|
68
|
+
|
|
69
|
+
------
|
|
70
|
+
|
|
71
|
+
## Minimal workflow (60 seconds)
|
|
72
|
+
1) Launch the app.
|
|
73
|
+
2) Enter lattice constants/angles and beam energy, or drop a CIF file.
|
|
74
|
+
3) Click **Initialize**.
|
|
75
|
+
4) Use the feature tabs (Scattering Geometry / Structure Factor) to calculate and visualize.
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
----
|
|
80
|
+
|
|
81
|
+
## Using the app
|
|
82
|
+
|
|
83
|
+
### 1. Initialization window
|
|
84
|
+
- Enter lattice constants (a, b, c) and angles (alpha, beta, gamma); beam energy auto-updates wavelength/|k|.
|
|
85
|
+
- Optional: drop a CIF to autofill lattice parameters and preview the unit cell.
|
|
86
|
+
- Adjust Euler angles (roll, pitch, yaw) to orient the sample relative to the scattering plane;
|
|
87
|
+
- Click **Initialize** to load the main interface and pass parameters to all tabs.
|
|
88
|
+
|
|
89
|
+
### 2. Scattering Geometry tab
|
|
90
|
+
- Angles → HKL: enter 2θ/θ/χ/φ, compute HKL.
|
|
91
|
+
- HKL → Angles: enter HKL, compute feasible angles.
|
|
92
|
+
- HK to Angles (fixed 2θ) and HKL scan (fixed 2θ) subtabs for trajectory planning.
|
|
93
|
+
|
|
94
|
+

|
|
95
|
+
|
|
96
|
+
### 3. Structure Factor tab
|
|
97
|
+
- Requires a CIF (from init) and an energy in the tab.
|
|
98
|
+
- HKL plane: explore a 3D HKL cube with linked HK/HL/KL slices.
|
|
99
|
+
- Customized plane: choose U/V vectors and a center to sample an arbitrary plane in reciprocal space.
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+
### 4. Resetting
|
|
104
|
+
Use the toolbar button or **File → Reset Parameters** to return to the init window, clear the CIF lock, and re-enter parameters.
|
|
105
|
+
|
|
106
|
+
------
|
|
107
|
+
|
|
108
|
+
## Project structure (at a glance)
|
|
109
|
+
```
|
|
110
|
+
advisor/ application package
|
|
111
|
+
app.py bootstrap
|
|
112
|
+
controllers/ app/feature coordinators
|
|
113
|
+
domain/ math and geometry helpers (no PyQt)
|
|
114
|
+
features/ per-feature domain/controller/ui code
|
|
115
|
+
ui/ shared UI pieces (init window, main window, base tab, visualizers)
|
|
116
|
+
resources/ QSS, icons, config JSON, sample data
|
|
117
|
+
docs/ Sphinx sources and assets
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Documentation
|
|
121
|
+
- Full Sphinx docs live in `docs/` and on *[Read the Docs](https://advisor-scattering.readthedocs.io/en/latest/)*.
|
|
122
|
+
- Appendix covers scattering angle definitions and HKL conventions.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Advisor-Scattering — Advanced Visual Scattering Toolkit for Reciprocal-space
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
Advisor-Scattering is a PyQt5 desktop app for X-ray scattering/diffraction experiments. It helps you convert scattering angles ↔ momentum transfer (HKL), explore scattering geometry, and visualize structure factors—all with interactive plots. Full docs on *[Read the Docs](https://advisor-scattering.readthedocs.io/en/latest/)*.
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
or use the link below to view the demo video.
|
|
10
|
+
▶ [Demo video (MP4)](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/showcase.mp4)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
- Convert scattering angles to momentum transfer (HKL) and vice versa.
|
|
15
|
+
- Visualize scattering geometry and unit cells
|
|
16
|
+
- Compute and visualize structure factors in reciprocal space.
|
|
17
|
+
- CIF file drop-in support
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
- Python 3.8+ with PyQt5, numpy, scipy, matplotlib, Dans_Diffraction (see `requirements.txt`).
|
|
22
|
+
|
|
23
|
+
From PyPI:
|
|
24
|
+
```bash
|
|
25
|
+
pip install advisor-scattering
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
From source:
|
|
29
|
+
```bash
|
|
30
|
+
python -m venv .venv
|
|
31
|
+
source .venv/bin/activate # .venv\Scripts\activate on Windows
|
|
32
|
+
pip install -r requirements.txt
|
|
33
|
+
pip install .
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Run
|
|
37
|
+
```bash
|
|
38
|
+
advisor-scattering
|
|
39
|
+
# or
|
|
40
|
+
advisor
|
|
41
|
+
# or
|
|
42
|
+
python -m advisor
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
*Note: the install command is `pip install advisor-scattering`, and the import is `import advisor`*.
|
|
46
|
+
|
|
47
|
+
------
|
|
48
|
+
|
|
49
|
+
## Minimal workflow (60 seconds)
|
|
50
|
+
1) Launch the app.
|
|
51
|
+
2) Enter lattice constants/angles and beam energy, or drop a CIF file.
|
|
52
|
+
3) Click **Initialize**.
|
|
53
|
+
4) Use the feature tabs (Scattering Geometry / Structure Factor) to calculate and visualize.
|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
----
|
|
58
|
+
|
|
59
|
+
## Using the app
|
|
60
|
+
|
|
61
|
+
### 1. Initialization window
|
|
62
|
+
- Enter lattice constants (a, b, c) and angles (alpha, beta, gamma); beam energy auto-updates wavelength/|k|.
|
|
63
|
+
- Optional: drop a CIF to autofill lattice parameters and preview the unit cell.
|
|
64
|
+
- Adjust Euler angles (roll, pitch, yaw) to orient the sample relative to the scattering plane;
|
|
65
|
+
- Click **Initialize** to load the main interface and pass parameters to all tabs.
|
|
66
|
+
|
|
67
|
+
### 2. Scattering Geometry tab
|
|
68
|
+
- Angles → HKL: enter 2θ/θ/χ/φ, compute HKL.
|
|
69
|
+
- HKL → Angles: enter HKL, compute feasible angles.
|
|
70
|
+
- HK to Angles (fixed 2θ) and HKL scan (fixed 2θ) subtabs for trajectory planning.
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
### 3. Structure Factor tab
|
|
75
|
+
- Requires a CIF (from init) and an energy in the tab.
|
|
76
|
+
- HKL plane: explore a 3D HKL cube with linked HK/HL/KL slices.
|
|
77
|
+
- Customized plane: choose U/V vectors and a center to sample an arbitrary plane in reciprocal space.
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
### 4. Resetting
|
|
82
|
+
Use the toolbar button or **File → Reset Parameters** to return to the init window, clear the CIF lock, and re-enter parameters.
|
|
83
|
+
|
|
84
|
+
------
|
|
85
|
+
|
|
86
|
+
## Project structure (at a glance)
|
|
87
|
+
```
|
|
88
|
+
advisor/ application package
|
|
89
|
+
app.py bootstrap
|
|
90
|
+
controllers/ app/feature coordinators
|
|
91
|
+
domain/ math and geometry helpers (no PyQt)
|
|
92
|
+
features/ per-feature domain/controller/ui code
|
|
93
|
+
ui/ shared UI pieces (init window, main window, base tab, visualizers)
|
|
94
|
+
resources/ QSS, icons, config JSON, sample data
|
|
95
|
+
docs/ Sphinx sources and assets
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Documentation
|
|
99
|
+
- Full Sphinx docs live in `docs/` and on *[Read the Docs](https://advisor-scattering.readthedocs.io/en/latest/)*.
|
|
100
|
+
- Appendix covers scattering angle definitions and HKL conventions.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Application bootstrap for Advisor-Scattering (Advanced Visual Scattering Toolkit for Reciprocal-space)."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from PyQt5.QtWidgets import QApplication
|
|
6
|
+
from PyQt5.QtCore import Qt, QLocale, QFile, QIODevice
|
|
7
|
+
|
|
8
|
+
from advisor.controllers import AppController
|
|
9
|
+
from advisor.resources import resources_rc
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_stylesheet() -> str:
|
|
13
|
+
"""Load QSS stylesheet if present."""
|
|
14
|
+
base_dir = os.path.dirname(os.path.abspath(__file__))
|
|
15
|
+
qss_path = os.path.join(base_dir, "resources", "qss", "styles.qss")
|
|
16
|
+
if os.path.exists(qss_path):
|
|
17
|
+
with open(qss_path, "r", encoding="utf-8") as handle:
|
|
18
|
+
return handle.read()
|
|
19
|
+
return ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main():
|
|
23
|
+
"""Main application entry point."""
|
|
24
|
+
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
25
|
+
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
26
|
+
|
|
27
|
+
app = QApplication(sys.argv)
|
|
28
|
+
app.setApplicationName("Advisor-Scattering")
|
|
29
|
+
QLocale.setDefault(QLocale(QLocale.English, QLocale.UnitedStates))
|
|
30
|
+
app.setStyleSheet(load_stylesheet())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
controller = AppController(app)
|
|
34
|
+
controller.show()
|
|
35
|
+
|
|
36
|
+
sys.exit(app.exec_())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Top-level application controller."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from PyQt5.QtWidgets import QApplication
|
|
8
|
+
|
|
9
|
+
from advisor.features import ScatteringGeometryController, StructureFactorController
|
|
10
|
+
from advisor.ui.init_window import InitWindow
|
|
11
|
+
from advisor.ui.main_window import MainWindow
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AppController:
|
|
15
|
+
"""Coordinates application startup and feature wiring."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, qt_app: QApplication):
|
|
18
|
+
self.app = qt_app
|
|
19
|
+
self.config = self._load_config()
|
|
20
|
+
self.parameters: Optional[dict] = None
|
|
21
|
+
|
|
22
|
+
self.main_window = MainWindow(controller=self)
|
|
23
|
+
self.init_window = InitWindow(controller=self)
|
|
24
|
+
self.main_window.attach_init_view(self.init_window)
|
|
25
|
+
|
|
26
|
+
self.features = [
|
|
27
|
+
ScatteringGeometryController(self),
|
|
28
|
+
StructureFactorController(self),
|
|
29
|
+
]
|
|
30
|
+
for feature in self.features:
|
|
31
|
+
self.main_window.add_feature_tab(
|
|
32
|
+
feature.view, feature.title, feature.icon, feature.description
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
self.init_window.initialized.connect(self.apply_parameters)
|
|
36
|
+
|
|
37
|
+
def show(self):
|
|
38
|
+
"""Show the main window."""
|
|
39
|
+
self.main_window.show()
|
|
40
|
+
|
|
41
|
+
def apply_parameters(self, params: dict):
|
|
42
|
+
"""Store and propagate global parameters, then show tabs."""
|
|
43
|
+
self.parameters = params
|
|
44
|
+
for feature in self.features:
|
|
45
|
+
feature.set_parameters(params)
|
|
46
|
+
self.main_window.show_tabs()
|
|
47
|
+
|
|
48
|
+
def reset_parameters(self):
|
|
49
|
+
"""Reset parameters and return to init view."""
|
|
50
|
+
self.parameters = None
|
|
51
|
+
if hasattr(self.init_window, "reset_inputs"):
|
|
52
|
+
self.init_window.reset_inputs()
|
|
53
|
+
self.main_window.show_init()
|
|
54
|
+
|
|
55
|
+
def get_parameters(self) -> dict:
|
|
56
|
+
"""Return the current global parameters."""
|
|
57
|
+
return self.parameters or {}
|
|
58
|
+
|
|
59
|
+
def _load_config(self) -> dict:
|
|
60
|
+
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
61
|
+
config_path = os.path.join(base_dir, "resources", "config", "app_config.json")
|
|
62
|
+
try:
|
|
63
|
+
with open(config_path, "r", encoding="utf-8") as handle:
|
|
64
|
+
return json.load(handle)
|
|
65
|
+
except Exception:
|
|
66
|
+
return {
|
|
67
|
+
"app_name": "Advisor-Scattering",
|
|
68
|
+
"window_size": {"width": 1200, "height": 800},
|
|
69
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Base feature controller."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from PyQt5.QtWidgets import QWidget
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FeatureController:
|
|
8
|
+
"""Base class for feature controllers."""
|
|
9
|
+
|
|
10
|
+
title: str = "Feature"
|
|
11
|
+
description: str = ""
|
|
12
|
+
icon: Optional[str] = None
|
|
13
|
+
|
|
14
|
+
def __init__(self, app_controller):
|
|
15
|
+
self.app_controller = app_controller
|
|
16
|
+
self.view: Optional[QWidget] = None
|
|
17
|
+
|
|
18
|
+
def set_parameters(self, params: dict):
|
|
19
|
+
"""Propagate global parameters to the feature."""
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
def build_view(self) -> QWidget:
|
|
23
|
+
"""Create and return the tab widget."""
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Domain layer with calculation and model logic."""
|
|
2
|
+
|
|
3
|
+
from .geometry import (
|
|
4
|
+
get_real_space_vectors,
|
|
5
|
+
get_reciprocal_space_vectors,
|
|
6
|
+
euler_to_matrix,
|
|
7
|
+
angle_to_matrix,
|
|
8
|
+
get_rotation,
|
|
9
|
+
sample_to_lab_conversion,
|
|
10
|
+
lab_to_sample_conversion,
|
|
11
|
+
)
|
|
12
|
+
from .unit_converter import UnitConverter
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"get_real_space_vectors",
|
|
16
|
+
"get_reciprocal_space_vectors",
|
|
17
|
+
"euler_to_matrix",
|
|
18
|
+
"angle_to_matrix",
|
|
19
|
+
"get_rotation",
|
|
20
|
+
"sample_to_lab_conversion",
|
|
21
|
+
"lab_to_sample_conversion",
|
|
22
|
+
"UnitConverter",
|
|
23
|
+
]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""This is a class for the lab."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from advisor.domain import angle_to_matrix
|
|
6
|
+
from .sample import Sample
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Lab:
|
|
11
|
+
"""This is a class for the lab."""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
"""Initialize the lab."""
|
|
15
|
+
self.sample = Sample()
|
|
16
|
+
|
|
17
|
+
self.theta = 0
|
|
18
|
+
self.phi = 0
|
|
19
|
+
self.chi = 0
|
|
20
|
+
self.a_vec_lab = None
|
|
21
|
+
self.b_vec_lab = None
|
|
22
|
+
self.c_vec_lab = None
|
|
23
|
+
self.a_star_vec_lab = None
|
|
24
|
+
self.b_star_vec_lab = None
|
|
25
|
+
self.c_star_vec_lab = None
|
|
26
|
+
|
|
27
|
+
def initialize(
|
|
28
|
+
self, a, b, c, alpha, beta, gamma, roll, pitch, yaw, theta, phi, chi
|
|
29
|
+
):
|
|
30
|
+
"""Initialize the lab."""
|
|
31
|
+
self.sample.initialize(a, b, c, alpha, beta, gamma, roll, pitch, yaw)
|
|
32
|
+
self.theta = theta
|
|
33
|
+
self.phi = phi
|
|
34
|
+
self.chi = chi
|
|
35
|
+
self.calculate_real_space_vectors()
|
|
36
|
+
self.calculate_reciprocal_space_vectors()
|
|
37
|
+
|
|
38
|
+
def get_sample_angles(self):
|
|
39
|
+
"""Get the sample angles."""
|
|
40
|
+
return self.theta, self.phi, self.chi
|
|
41
|
+
|
|
42
|
+
def get_lattice_angles(self):
|
|
43
|
+
"""get the lattice euler angles: roll pitch and yaw"""
|
|
44
|
+
return self.sample.get_lattice_angles()
|
|
45
|
+
|
|
46
|
+
def get_lattice_parameters(self):
|
|
47
|
+
"""Get the parameters of the sample."""
|
|
48
|
+
return self.sample.get_lattice_parameters()
|
|
49
|
+
|
|
50
|
+
def get_real_space_vectors(self, is_normalized=False):
|
|
51
|
+
"""Get the real space vectors in the lab frame."""
|
|
52
|
+
if is_normalized:
|
|
53
|
+
return self.a_vec_lab / np.linalg.norm(self.a_vec_lab), self.b_vec_lab / np.linalg.norm(self.b_vec_lab), self.c_vec_lab / np.linalg.norm(self.c_vec_lab)
|
|
54
|
+
return self.a_vec_lab, self.b_vec_lab, self.c_vec_lab
|
|
55
|
+
|
|
56
|
+
def get_reciprocal_space_vectors(self, is_normalized=False):
|
|
57
|
+
"""Get the reciprocal space vectors in the lab frame."""
|
|
58
|
+
if is_normalized:
|
|
59
|
+
return self.a_star_vec_lab / np.linalg.norm(self.a_star_vec_lab), self.b_star_vec_lab / np.linalg.norm(self.b_star_vec_lab), self.c_star_vec_lab / np.linalg.norm(self.c_star_vec_lab)
|
|
60
|
+
return self.a_star_vec_lab, self.b_star_vec_lab, self.c_star_vec_lab
|
|
61
|
+
|
|
62
|
+
def calculate_real_space_vectors(self):
|
|
63
|
+
"""Get the real space vectors in the lab frame."""
|
|
64
|
+
a_vec_sample, b_vec_sample, c_vec_sample = self.sample.get_real_space_vectors()
|
|
65
|
+
rotation_matrix = angle_to_matrix(self.theta, self.phi, self.chi)
|
|
66
|
+
self.a_vec_lab = rotation_matrix @ a_vec_sample
|
|
67
|
+
self.b_vec_lab = rotation_matrix @ b_vec_sample
|
|
68
|
+
self.c_vec_lab = rotation_matrix @ c_vec_sample
|
|
69
|
+
|
|
70
|
+
def calculate_reciprocal_space_vectors(self):
|
|
71
|
+
"""Get the reciprocal space vectors in the lab frame."""
|
|
72
|
+
a_star_vec_sample, b_star_vec_sample, c_star_vec_sample = (
|
|
73
|
+
self.sample.get_reciprocal_space_vectors()
|
|
74
|
+
)
|
|
75
|
+
rotation_matrix = angle_to_matrix(self.theta, self.phi, self.chi)
|
|
76
|
+
self.a_star_vec_lab = rotation_matrix @ a_star_vec_sample
|
|
77
|
+
self.b_star_vec_lab = rotation_matrix @ b_star_vec_sample
|
|
78
|
+
self.c_star_vec_lab = rotation_matrix @ c_star_vec_sample
|
|
79
|
+
|
|
80
|
+
def get_lab_basis(self):
|
|
81
|
+
"""Get the lab orthogonal basis vectors, in the lab frame."""
|
|
82
|
+
ex_lab = np.array([1, 0, 0])
|
|
83
|
+
ey_lab = np.array([0, 1, 0])
|
|
84
|
+
ez_lab = np.array([0, 0, 1])
|
|
85
|
+
return ex_lab, ey_lab, ez_lab
|
|
86
|
+
|
|
87
|
+
def get_sample_basis(self):
|
|
88
|
+
"""Get the sample orthogonal basis vectors, in the lab frame."""
|
|
89
|
+
ex_sample_in_sample, ey_sample_in_sample, ez_sample_in_sample = (
|
|
90
|
+
self.sample.get_sample_basis()
|
|
91
|
+
) # sample basis in the sample frame
|
|
92
|
+
|
|
93
|
+
rotation_matrix = angle_to_matrix(self.theta, self.phi, self.chi)
|
|
94
|
+
|
|
95
|
+
# sample basis in the lab frame
|
|
96
|
+
ex_sample_in_lab = rotation_matrix @ ex_sample_in_sample
|
|
97
|
+
ey_sample_in_lab = rotation_matrix @ ey_sample_in_sample
|
|
98
|
+
ez_sample_in_lab = rotation_matrix @ ez_sample_in_sample
|
|
99
|
+
return ex_sample_in_lab, ey_sample_in_lab, ez_sample_in_lab
|
|
100
|
+
|
|
101
|
+
def get_lattice_basis(self):
|
|
102
|
+
"""Get the lattice orthogonal basis vectors, in the lab frame."""
|
|
103
|
+
ex_lattice_in_sample, ey_lattice_in_sample, ez_lattice_in_sample = (
|
|
104
|
+
self.sample.get_lattice_basis()
|
|
105
|
+
) # lattice basis in the sample frame
|
|
106
|
+
|
|
107
|
+
rotation_matrix = angle_to_matrix(self.theta, self.phi, self.chi)
|
|
108
|
+
|
|
109
|
+
# lattice basis in the lab frame
|
|
110
|
+
ex_lattice_in_lab = rotation_matrix @ ex_lattice_in_sample
|
|
111
|
+
ey_lattice_in_lab = rotation_matrix @ ey_lattice_in_sample
|
|
112
|
+
ez_lattice_in_lab = rotation_matrix @ ez_lattice_in_sample
|
|
113
|
+
return ex_lattice_in_lab, ey_lattice_in_lab, ez_lattice_in_lab
|
|
114
|
+
|
|
115
|
+
def rotate(self, theta, phi, chi):
|
|
116
|
+
"""Rotate the lab."""
|
|
117
|
+
self.theta = theta
|
|
118
|
+
self.phi = phi
|
|
119
|
+
self.chi = chi
|
|
120
|
+
self.calculate_real_space_vectors()
|
|
121
|
+
self.calculate_reciprocal_space_vectors()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""This is a module for the lattice class."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from advisor.domain import (
|
|
6
|
+
get_real_space_vectors,
|
|
7
|
+
get_reciprocal_space_vectors,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Lattice:
|
|
12
|
+
"""This is a class for the lattice."""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
"""Initialize the sample."""
|
|
16
|
+
self.a = None
|
|
17
|
+
self.b = None
|
|
18
|
+
self.c = None
|
|
19
|
+
self.alpha = None
|
|
20
|
+
self.beta = None
|
|
21
|
+
self.gamma = None
|
|
22
|
+
|
|
23
|
+
# vectors in the lattice frame
|
|
24
|
+
self.a_vec_lattice = None
|
|
25
|
+
self.b_vec_lattice = None
|
|
26
|
+
self.c_vec_lattice = None
|
|
27
|
+
|
|
28
|
+
# reciprocal vectors in the lattice frame
|
|
29
|
+
self.a_star_vec_lattice = None
|
|
30
|
+
self.b_star_vec_lattice = None
|
|
31
|
+
self.c_star_vec_lattice = None
|
|
32
|
+
|
|
33
|
+
def initialize(self, a, b, c, alpha, beta, gamma):
|
|
34
|
+
"""Initialize the sample.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
a, b, c (float): Lattice constants in Angstroms
|
|
38
|
+
alpha, beta, gamma (float): Lattice angles in degrees
|
|
39
|
+
roll, pitch, yaw (float): Euler angles in degrees
|
|
40
|
+
"""
|
|
41
|
+
# First set the lattice parameters
|
|
42
|
+
self.a = a
|
|
43
|
+
self.b = b
|
|
44
|
+
self.c = c
|
|
45
|
+
self.alpha = alpha
|
|
46
|
+
self.beta = beta
|
|
47
|
+
self.gamma = gamma
|
|
48
|
+
|
|
49
|
+
# Then calculate vectors in sample coordinate system
|
|
50
|
+
self.a_vec_lattice, self.b_vec_lattice, self.c_vec_lattice = (
|
|
51
|
+
get_real_space_vectors(a, b, c, alpha, beta, gamma)
|
|
52
|
+
)
|
|
53
|
+
self.a_star_vec_lattice, self.b_star_vec_lattice, self.c_star_vec_lattice = (
|
|
54
|
+
get_reciprocal_space_vectors(a, b, c, alpha, beta, gamma)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def get_lattice_parameters(self):
|
|
58
|
+
"""Get the parameters of the sample. if None, raise "initialize the sample first"."""
|
|
59
|
+
try:
|
|
60
|
+
a, b, c = self.a, self.b, self.c
|
|
61
|
+
alpha, beta, gamma = self.alpha, self.beta, self.gamma
|
|
62
|
+
return a, b, c, alpha, beta, gamma
|
|
63
|
+
except KeyError as exc:
|
|
64
|
+
raise ValueError("initialize the sample first") from exc
|
|
65
|
+
|
|
66
|
+
def get_real_space_vectors(self):
|
|
67
|
+
"""Get the real space vectors in the lattice frame."""
|
|
68
|
+
return self.a_vec_lattice, self.b_vec_lattice, self.c_vec_lattice
|
|
69
|
+
|
|
70
|
+
def get_reciprocal_space_vectors(self):
|
|
71
|
+
"""Get the reciprocal space vectors in the lattice frame."""
|
|
72
|
+
return self.a_star_vec_lattice, self.b_star_vec_lattice, self.c_star_vec_lattice
|
|
73
|
+
|
|
74
|
+
def get_lattice_basis(self):
|
|
75
|
+
"""Get the lattice orthogonal basis vectors, in the lattice frame."""
|
|
76
|
+
ex_lattice_in_lattice = np.array([1, 0, 0])
|
|
77
|
+
ey_lattice_in_lattice = np.array([0, 1, 0])
|
|
78
|
+
ez_lattice_in_lattice = np.array([0, 0, 1])
|
|
79
|
+
return ex_lattice_in_lattice, ey_lattice_in_lattice, ez_lattice_in_lattice
|