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.
Files changed (75) hide show
  1. advisor_scattering-0.5.0/MANIFEST.in +2 -0
  2. advisor_scattering-0.5.0/PKG-INFO +122 -0
  3. advisor_scattering-0.5.0/README.md +100 -0
  4. advisor_scattering-0.5.0/advisor/__init__.py +3 -0
  5. advisor_scattering-0.5.0/advisor/__main__.py +7 -0
  6. advisor_scattering-0.5.0/advisor/app.py +40 -0
  7. advisor_scattering-0.5.0/advisor/controllers/__init__.py +6 -0
  8. advisor_scattering-0.5.0/advisor/controllers/app_controller.py +69 -0
  9. advisor_scattering-0.5.0/advisor/controllers/feature_controller.py +25 -0
  10. advisor_scattering-0.5.0/advisor/domain/__init__.py +23 -0
  11. advisor_scattering-0.5.0/advisor/domain/core/__init__.py +8 -0
  12. advisor_scattering-0.5.0/advisor/domain/core/lab.py +121 -0
  13. advisor_scattering-0.5.0/advisor/domain/core/lattice.py +79 -0
  14. advisor_scattering-0.5.0/advisor/domain/core/sample.py +101 -0
  15. advisor_scattering-0.5.0/advisor/domain/geometry.py +212 -0
  16. advisor_scattering-0.5.0/advisor/domain/unit_converter.py +82 -0
  17. advisor_scattering-0.5.0/advisor/features/__init__.py +6 -0
  18. advisor_scattering-0.5.0/advisor/features/scattering_geometry/controllers/__init__.py +5 -0
  19. advisor_scattering-0.5.0/advisor/features/scattering_geometry/controllers/scattering_geometry_controller.py +26 -0
  20. advisor_scattering-0.5.0/advisor/features/scattering_geometry/domain/__init__.py +5 -0
  21. advisor_scattering-0.5.0/advisor/features/scattering_geometry/domain/brillouin_calculator.py +410 -0
  22. advisor_scattering-0.5.0/advisor/features/scattering_geometry/domain/core.py +516 -0
  23. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/__init__.py +5 -0
  24. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/__init__.py +17 -0
  25. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/angles_to_hkl_components.py +150 -0
  26. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/hk_angles_components.py +430 -0
  27. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/hkl_scan_components.py +526 -0
  28. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/components/hkl_to_angles_components.py +315 -0
  29. advisor_scattering-0.5.0/advisor/features/scattering_geometry/ui/scattering_geometry_tab.py +725 -0
  30. advisor_scattering-0.5.0/advisor/features/structure_factor/controllers/__init__.py +6 -0
  31. advisor_scattering-0.5.0/advisor/features/structure_factor/controllers/structure_factor_controller.py +25 -0
  32. advisor_scattering-0.5.0/advisor/features/structure_factor/domain/__init__.py +6 -0
  33. advisor_scattering-0.5.0/advisor/features/structure_factor/domain/structure_factor_calculator.py +107 -0
  34. advisor_scattering-0.5.0/advisor/features/structure_factor/ui/__init__.py +6 -0
  35. advisor_scattering-0.5.0/advisor/features/structure_factor/ui/components/__init__.py +12 -0
  36. advisor_scattering-0.5.0/advisor/features/structure_factor/ui/components/customized_plane_components.py +358 -0
  37. advisor_scattering-0.5.0/advisor/features/structure_factor/ui/components/hkl_plane_components.py +391 -0
  38. advisor_scattering-0.5.0/advisor/features/structure_factor/ui/structure_factor_tab.py +273 -0
  39. advisor_scattering-0.5.0/advisor/resources/__init__.py +0 -0
  40. advisor_scattering-0.5.0/advisor/resources/config/app_config.json +14 -0
  41. advisor_scattering-0.5.0/advisor/resources/config/tips.json +4 -0
  42. advisor_scattering-0.5.0/advisor/resources/data/nacl.cif +111 -0
  43. advisor_scattering-0.5.0/advisor/resources/icons/bz_caculator.jpg +0 -0
  44. advisor_scattering-0.5.0/advisor/resources/icons/bz_calculator.png +0 -0
  45. advisor_scattering-0.5.0/advisor/resources/icons/minus.svg +3 -0
  46. advisor_scattering-0.5.0/advisor/resources/icons/placeholder.png +0 -0
  47. advisor_scattering-0.5.0/advisor/resources/icons/plus.svg +3 -0
  48. advisor_scattering-0.5.0/advisor/resources/icons/reset.png +0 -0
  49. advisor_scattering-0.5.0/advisor/resources/icons/sf_calculator.jpg +0 -0
  50. advisor_scattering-0.5.0/advisor/resources/icons/sf_calculator.png +0 -0
  51. advisor_scattering-0.5.0/advisor/resources/icons.qrc +6 -0
  52. advisor_scattering-0.5.0/advisor/resources/qss/styles.qss +348 -0
  53. advisor_scattering-0.5.0/advisor/resources/resources_rc.py +83 -0
  54. advisor_scattering-0.5.0/advisor/ui/__init__.py +7 -0
  55. advisor_scattering-0.5.0/advisor/ui/init_window.py +566 -0
  56. advisor_scattering-0.5.0/advisor/ui/main_window.py +174 -0
  57. advisor_scattering-0.5.0/advisor/ui/tab_interface.py +44 -0
  58. advisor_scattering-0.5.0/advisor/ui/tips.py +30 -0
  59. advisor_scattering-0.5.0/advisor/ui/utils/__init__.py +6 -0
  60. advisor_scattering-0.5.0/advisor/ui/utils/readcif.py +129 -0
  61. advisor_scattering-0.5.0/advisor/ui/visualizers/HKLScan2DVisualizer.py +224 -0
  62. advisor_scattering-0.5.0/advisor/ui/visualizers/__init__.py +8 -0
  63. advisor_scattering-0.5.0/advisor/ui/visualizers/coordinate_visualizer.py +203 -0
  64. advisor_scattering-0.5.0/advisor/ui/visualizers/scattering_visualizer.py +301 -0
  65. advisor_scattering-0.5.0/advisor/ui/visualizers/structure_factor_visualizer.py +426 -0
  66. advisor_scattering-0.5.0/advisor/ui/visualizers/structure_factor_visualizer_2d.py +235 -0
  67. advisor_scattering-0.5.0/advisor/ui/visualizers/unitcell_visualizer.py +518 -0
  68. advisor_scattering-0.5.0/advisor_scattering.egg-info/PKG-INFO +122 -0
  69. advisor_scattering-0.5.0/advisor_scattering.egg-info/SOURCES.txt +73 -0
  70. advisor_scattering-0.5.0/advisor_scattering.egg-info/dependency_links.txt +1 -0
  71. advisor_scattering-0.5.0/advisor_scattering.egg-info/entry_points.txt +3 -0
  72. advisor_scattering-0.5.0/advisor_scattering.egg-info/requires.txt +5 -0
  73. advisor_scattering-0.5.0/advisor_scattering.egg-info/top_level.txt +1 -0
  74. advisor_scattering-0.5.0/pyproject.toml +47 -0
  75. advisor_scattering-0.5.0/setup.cfg +4 -0
@@ -0,0 +1,2 @@
1
+ include README.md
2
+ recursive-include advisor/resources *
@@ -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
+ ![Python](https://img.shields.io/badge/python-3.9+-blue.svg)
26
+ ![PyQt5](https://img.shields.io/badge/PyQt5-5.15+-green.svg)
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
+ ![Demo video](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/showcase.gif)
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
+ ![Init flow](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/init.gif)
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
+ ![Scattering geometry demo](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/scattering_geometry_tab_demo.gif)
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
+ ![Structure factor demo](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/structure_factor_tab_demo.gif)
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
+ ![Python](https://img.shields.io/badge/python-3.9+-blue.svg)
4
+ ![PyQt5](https://img.shields.io/badge/PyQt5-5.15+-green.svg)
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
+ ![Demo video](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/showcase.gif)
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
+ ![Init flow](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/init.gif)
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
+ ![Scattering geometry demo](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/scattering_geometry_tab_demo.gif)
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
+ ![Structure factor demo](https://raw.githubusercontent.com/HongXunyang/advisor/main/docs/source/_static/structure_factor_tab_demo.gif)
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,3 @@
1
+ """Advisor-Scattering: Advanced Visual Scattering Toolkit for Reciprocal-space."""
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,7 @@
1
+ """Entrypoint for running Advisor-Scattering as a module."""
2
+
3
+ from .app import main
4
+
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -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,6 @@
1
+ """Controllers coordinating views and domain logic."""
2
+
3
+ from .feature_controller import FeatureController
4
+ from .app_controller import AppController
5
+
6
+ __all__ = ["AppController", "FeatureController"]
@@ -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,8 @@
1
+ """Core structural classes (Lattice, Sample, Lab)."""
2
+
3
+ from .lattice import Lattice
4
+ from .sample import Sample
5
+ from .lab import Lab
6
+
7
+ __all__ = ["Lattice", "Sample", "Lab"]
8
+
@@ -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