ScatTools 0.0.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.
- scattools-0.0.1/.github/workflows/docs.yml +43 -0
- scattools-0.0.1/.github/workflows/publish.yml +30 -0
- scattools-0.0.1/.gitignore +12 -0
- scattools-0.0.1/.idea/.gitignore +8 -0
- scattools-0.0.1/.idea/.name +1 -0
- scattools-0.0.1/.idea/ScatTools.iml +8 -0
- scattools-0.0.1/.idea/inspectionProfiles/Project_Default.xml +12 -0
- scattools-0.0.1/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- scattools-0.0.1/.idea/misc.xml +4 -0
- scattools-0.0.1/.idea/modules.xml +8 -0
- scattools-0.0.1/.idea/vcs.xml +6 -0
- scattools-0.0.1/.idea/workspace.xml +52 -0
- scattools-0.0.1/PKG-INFO +43 -0
- scattools-0.0.1/README.md +28 -0
- scattools-0.0.1/docs/Makefile +20 -0
- scattools-0.0.1/docs/conf.py +43 -0
- scattools-0.0.1/docs/gid.rst +7 -0
- scattools-0.0.1/docs/index.rst +17 -0
- scattools-0.0.1/docs/modules.rst +8 -0
- scattools-0.0.1/docs/sas.rst +7 -0
- scattools-0.0.1/pyproject.toml +29 -0
- scattools-0.0.1/src/__init__.py +6 -0
- scattools-0.0.1/src/gid/__init__.py +2 -0
- scattools-0.0.1/src/gid/gid.py +154 -0
- scattools-0.0.1/src/sas/__init__.py +2 -0
- scattools-0.0.1/src/sas/sas.py +96 -0
- scattools-0.0.1/usage_examples.ipynb +139 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: '3.12'
|
|
22
|
+
- name: Install dependencies
|
|
23
|
+
run: |
|
|
24
|
+
python -m pip install --upgrade pip
|
|
25
|
+
pip install sphinx sphinx-rtd-theme numpy matplotlib h5py
|
|
26
|
+
- name: Build documentation
|
|
27
|
+
run: |
|
|
28
|
+
make docs
|
|
29
|
+
- name: Upload artifact
|
|
30
|
+
uses: actions/upload-pages-artifact@v3
|
|
31
|
+
with:
|
|
32
|
+
path: docs/_build/html
|
|
33
|
+
|
|
34
|
+
deploy:
|
|
35
|
+
environment:
|
|
36
|
+
name: github-pages
|
|
37
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
needs: build
|
|
40
|
+
steps:
|
|
41
|
+
- name: Deploy to GitHub Pages
|
|
42
|
+
id: deployment
|
|
43
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-and-publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.x'
|
|
20
|
+
|
|
21
|
+
- name: Install build dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install build
|
|
25
|
+
|
|
26
|
+
- name: Build package
|
|
27
|
+
run: python -m build
|
|
28
|
+
|
|
29
|
+
- name: Publish package
|
|
30
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ScatTools
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="PYTHON_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$/../ScatTools" />
|
|
5
|
+
<orderEntry type="jdk" jdkName="Python 3.12 virtualenv at ~/Desktop/Xray" jdkType="Python SDK" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<component name="InspectionProjectProfileManager">
|
|
2
|
+
<profile version="1.0">
|
|
3
|
+
<option name="myName" value="Project Default" />
|
|
4
|
+
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
|
5
|
+
<option name="ignoredIdentifiers">
|
|
6
|
+
<list>
|
|
7
|
+
<option value="lmfit.minimizer.MinimizerResult.params" />
|
|
8
|
+
</list>
|
|
9
|
+
</option>
|
|
10
|
+
</inspection_tool>
|
|
11
|
+
</profile>
|
|
12
|
+
</component>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ChangeListManager">
|
|
4
|
+
<list default="true" id="09f946be-75db-402c-b25b-84222a289980" name="Changes" comment="" />
|
|
5
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
6
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
7
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
8
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
9
|
+
</component>
|
|
10
|
+
<component name="Git.Settings">
|
|
11
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
12
|
+
</component>
|
|
13
|
+
<component name="ProjectColorInfo"><![CDATA[{
|
|
14
|
+
"associatedIndex": 5
|
|
15
|
+
}]]></component>
|
|
16
|
+
<component name="ProjectId" id="3HAdiFu7fWFBVf6cvVccj9EsM0S" />
|
|
17
|
+
<component name="ProjectViewState">
|
|
18
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
19
|
+
<option name="showLibraryContents" value="true" />
|
|
20
|
+
</component>
|
|
21
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
22
|
+
"keyToString": {
|
|
23
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
24
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
25
|
+
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true",
|
|
26
|
+
"git-widget-placeholder": "master",
|
|
27
|
+
"junie.onboarding.icon.badge.shown": "true",
|
|
28
|
+
"nodejs_package_manager_path": "npm",
|
|
29
|
+
"to.speed.mode.migration.done": "true",
|
|
30
|
+
"vue.rearranger.settings.migration": "true"
|
|
31
|
+
}
|
|
32
|
+
}]]></component>
|
|
33
|
+
<component name="SharedIndexes">
|
|
34
|
+
<attachedChunks>
|
|
35
|
+
<set>
|
|
36
|
+
<option value="bundled-js-predefined-d6986cc7102b-a71380e98a7c-JavaScript-PY-252.28238.29" />
|
|
37
|
+
<option value="bundled-python-sdk-a101402ee41c-968fe969f28a-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.28238.29" />
|
|
38
|
+
</set>
|
|
39
|
+
</attachedChunks>
|
|
40
|
+
</component>
|
|
41
|
+
<component name="TaskManager">
|
|
42
|
+
<task active="true" id="Default" summary="Default task">
|
|
43
|
+
<changelist id="09f946be-75db-402c-b25b-84222a289980" name="Changes" comment="" />
|
|
44
|
+
<created>1785314933654</created>
|
|
45
|
+
<option name="number" value="Default" />
|
|
46
|
+
<option name="presentableId" value="Default" />
|
|
47
|
+
<updated>1785314933654</updated>
|
|
48
|
+
<workItem from="1785314934765" duration="1000" />
|
|
49
|
+
</task>
|
|
50
|
+
<servers />
|
|
51
|
+
</component>
|
|
52
|
+
</project>
|
scattools-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ScatTools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Tools for X-ray and neutron scattering analysis
|
|
5
|
+
Project-URL: Homepage, https://github.com/chelberserker/ScatTools/
|
|
6
|
+
Author: Egor A. Bersenev
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Requires-Dist: h5py
|
|
12
|
+
Requires-Dist: matplotlib
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Scatter Analysis
|
|
17
|
+
|
|
18
|
+
A Python package providing tools for the analysis of X-ray and neutron scattering data (SAXS, SANS, and GID).
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install ScatTools
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **GID**: Analysis of Grazing Incidence Diffraction data. Calculates crystallographic parameters based on scattering vectors.
|
|
29
|
+
- **SAS**: Analysis of Small Angle Scattering data, including Small Angle X-ray Scattering (SAXS) and Small Angle Neutron Scattering (SANS) with support for plotting and data loading.
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
Documentation will be made available shortly.
|
|
34
|
+
|
|
35
|
+
## Usage Example
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from gid import calculate_cell_parameters
|
|
39
|
+
|
|
40
|
+
# Calculate parameters based on scattering vectors
|
|
41
|
+
params = calculate_cell_parameters(qxy1=1.0, qxy2=1.0, qxy3=1.0, qz1=0.1, qz2=0.1, qz3=0.1)
|
|
42
|
+
print(params)
|
|
43
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Scatter Analysis
|
|
2
|
+
|
|
3
|
+
A Python package providing tools for the analysis of X-ray and neutron scattering data (SAXS, SANS, and GID).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ScatTools
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **GID**: Analysis of Grazing Incidence Diffraction data. Calculates crystallographic parameters based on scattering vectors.
|
|
14
|
+
- **SAS**: Analysis of Small Angle Scattering data, including Small Angle X-ray Scattering (SAXS) and Small Angle Neutron Scattering (SANS) with support for plotting and data loading.
|
|
15
|
+
|
|
16
|
+
## Documentation
|
|
17
|
+
|
|
18
|
+
Documentation will be made available shortly.
|
|
19
|
+
|
|
20
|
+
## Usage Example
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from gid import calculate_cell_parameters
|
|
24
|
+
|
|
25
|
+
# Calculate parameters based on scattering vectors
|
|
26
|
+
params = calculate_cell_parameters(qxy1=1.0, qxy2=1.0, qxy3=1.0, qz1=0.1, qz2=0.1, qz3=0.1)
|
|
27
|
+
print(params)
|
|
28
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Minimal makefile for Sphinx documentation
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
# You can set these variables from the command line, and also
|
|
5
|
+
# from the environment for the first two.
|
|
6
|
+
SPHINXOPTS ?=
|
|
7
|
+
SPHINXBUILD ?= sphinx-build
|
|
8
|
+
SOURCEDIR = .
|
|
9
|
+
BUILDDIR = _build
|
|
10
|
+
|
|
11
|
+
# Put it first so that "make" without argument is like "make help".
|
|
12
|
+
help:
|
|
13
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
14
|
+
|
|
15
|
+
.PHONY: help Makefile
|
|
16
|
+
|
|
17
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
18
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
19
|
+
%: Makefile
|
|
20
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
4
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
sys.path.insert(0, os.path.abspath('../src'))
|
|
9
|
+
|
|
10
|
+
# -- Project information -----------------------------------------------------
|
|
11
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
12
|
+
|
|
13
|
+
project = 'ScatTools'
|
|
14
|
+
copyright = '2025, Egor A. Bersenev'
|
|
15
|
+
author = 'Egor A. Bersenev'
|
|
16
|
+
|
|
17
|
+
# -- General configuration ---------------------------------------------------
|
|
18
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
19
|
+
|
|
20
|
+
extensions = [
|
|
21
|
+
'sphinx.ext.autodoc',
|
|
22
|
+
'sphinx.ext.viewcode',
|
|
23
|
+
'sphinx.ext.napoleon',
|
|
24
|
+
'sphinx_rtd_theme',
|
|
25
|
+
'nbsphinx',
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
autodoc_member_order = 'groupwise'
|
|
29
|
+
|
|
30
|
+
autodoc_default_options = {
|
|
31
|
+
'members': True, # Document members (methods/attributes)
|
|
32
|
+
'member-order': 'groupwise', # Enforce grouping by type
|
|
33
|
+
'undoc-members': True, # Include members without docstrings
|
|
34
|
+
'show-inheritance': True, # Show parent classes
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
autodoc_typehints = "description" # Moves type hints into the description instead of signature
|
|
38
|
+
|
|
39
|
+
templates_path = ['_templates']
|
|
40
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
41
|
+
|
|
42
|
+
html_theme = 'classic'
|
|
43
|
+
html_static_path = ['_static']
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.. scatter-analysis documentation master file
|
|
2
|
+
|
|
3
|
+
Welcome to scatter-analysis's documentation!
|
|
4
|
+
============================================
|
|
5
|
+
|
|
6
|
+
.. toctree::
|
|
7
|
+
:maxdepth: 2
|
|
8
|
+
:caption: Contents:
|
|
9
|
+
|
|
10
|
+
modules
|
|
11
|
+
|
|
12
|
+
Indices and tables
|
|
13
|
+
==================
|
|
14
|
+
|
|
15
|
+
* :ref:`genindex`
|
|
16
|
+
* :ref:`modindex`
|
|
17
|
+
* :ref:`search`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ScatTools"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Tools for X-ray and neutron scattering analysis"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Egor A. Bersenev" },
|
|
13
|
+
]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"numpy",
|
|
16
|
+
"matplotlib",
|
|
17
|
+
"h5py",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/chelberserker/ScatTools/"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/gid", "src/sas"]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def calculate_cell_parameters(qxy1, qxy2, qxy3, qz1, qz2, qz3):
|
|
11
|
+
"""
|
|
12
|
+
Calculates crystallographic parameters based on scattering vectors.
|
|
13
|
+
Args:
|
|
14
|
+
qxy1 (float): q_{xy} position of the first peak
|
|
15
|
+
qxy2 (float): q_{xy} position of the second peak
|
|
16
|
+
qxy3 (float): q_{xy} position of the third peak
|
|
17
|
+
qz1 (float): q_{z} position of the first peak
|
|
18
|
+
qz2 (float): q_{z} position of the second peak
|
|
19
|
+
qz3 (float): q_{z} position of the third peak
|
|
20
|
+
Returns:
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
vectors = sorted([
|
|
25
|
+
(qxy1, qz1),
|
|
26
|
+
(qxy2, qz2),
|
|
27
|
+
(qxy3, qz3)
|
|
28
|
+
], key=lambda v: v[0])
|
|
29
|
+
|
|
30
|
+
qx10, qz10 = vectors[0]
|
|
31
|
+
qx01, qz01 = vectors[1]
|
|
32
|
+
qx11, qz11 = vectors[2]
|
|
33
|
+
|
|
34
|
+
cos_g = (qx10 ** 2 + qx01 ** 2 - qx11 ** 2) / (2 * qx10 * qx01)
|
|
35
|
+
if abs(cos_g) > 1.0:
|
|
36
|
+
logger.warning('Error: Check input values (cos G > 1).')
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
gam_rad = np.acos(cos_g) # Angle between A and B in real space
|
|
40
|
+
|
|
41
|
+
a = 2 * np.pi / (qx10 * np.sin(gam_rad))
|
|
42
|
+
b = 2 * np.pi / (qx01 * np.sin(gam_rad))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if abs(qz10 + qz01 - qz11) > 0.03:
|
|
46
|
+
qz10 = -qz10
|
|
47
|
+
|
|
48
|
+
if abs(qz10 + qz01 - qz11) > 0.03:
|
|
49
|
+
qz11 = -qz11
|
|
50
|
+
|
|
51
|
+
# Normal components
|
|
52
|
+
nz = qx10 * qx01 * np.sin(gam_rad)
|
|
53
|
+
nx = -qz10 * qx01 * np.sin(gam_rad)
|
|
54
|
+
ny = -(qx10 * qz01 + qz10 * qx01 * np.cos(gam_rad))
|
|
55
|
+
nmod = np.sqrt(nx ** 2 + ny ** 2 + nz ** 2)
|
|
56
|
+
|
|
57
|
+
if nmod == 0 or abs(nz / nmod) > 1.0:
|
|
58
|
+
logger.warning('Error: Check input values (Nz/Nmod > 1).')
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
t3_rad = math.acos(nz / nmod)
|
|
62
|
+
|
|
63
|
+
# Azimuth: Angle between projection of N on XOY where OX==a* and OY==b*
|
|
64
|
+
if t3_rad == 0.0:
|
|
65
|
+
azimuth3_rad = 0.0
|
|
66
|
+
else:
|
|
67
|
+
if nx == 0.0:
|
|
68
|
+
azimuth3_rad = 0.0
|
|
69
|
+
else:
|
|
70
|
+
azimuth3_rad = np.atan(ny / nx)
|
|
71
|
+
|
|
72
|
+
area = a * b * np.sin(gam_rad)
|
|
73
|
+
|
|
74
|
+
# Convert angles to degrees
|
|
75
|
+
gam_deg = np.rad2deg(gam_rad)
|
|
76
|
+
if gam_deg > 180.0:
|
|
77
|
+
gam_deg = 360.0 - gam_deg
|
|
78
|
+
|
|
79
|
+
t3_deg = np.rad2deg(t3_rad)
|
|
80
|
+
|
|
81
|
+
# Azimuth = counter-clockwise rotation from real space A-vector
|
|
82
|
+
if azimuth3_rad != 0.0:
|
|
83
|
+
azimuth3_deg = np.rad2deg(azimuth3_rad) + (gam_deg - 90.0)
|
|
84
|
+
else:
|
|
85
|
+
azimuth3_deg = 0.0
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
"a": np.round(a, 5),
|
|
89
|
+
"b": np.round(b, 5),
|
|
90
|
+
"gamma": np.round(gam_deg,2),
|
|
91
|
+
"tilt": np.round(t3_deg,2),
|
|
92
|
+
"azimuth": np.round(azimuth3_deg,2),
|
|
93
|
+
"area": np.round(area, 3)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def calculate_q_values(a, b, gamma, tilt, azimuth):
|
|
97
|
+
"""
|
|
98
|
+
Calculates the scattering vectors (qxy, qz) based on 2D crystallographic parameters.
|
|
99
|
+
Args:
|
|
100
|
+
a (float): Unit cell parameter a
|
|
101
|
+
b (float): Unit cell parameter b
|
|
102
|
+
gamma (float): Angle between a and b in real space (degrees)
|
|
103
|
+
tilt (float): Tilt angle of the lattice (degrees)
|
|
104
|
+
azimuth (float): Azimuthal angle of the tilt (degrees)
|
|
105
|
+
Returns:
|
|
106
|
+
dict: The qxy and qz scattering vectors for the first three peaks
|
|
107
|
+
"""
|
|
108
|
+
gam_rad = np.deg2rad(gamma)
|
|
109
|
+
t3_rad = np.deg2rad(tilt)
|
|
110
|
+
|
|
111
|
+
# 1. Reverse A and B reciprocal relationships to get qxy1 (qx10) and qxy2 (qx01)
|
|
112
|
+
qx10 = 2 * np.pi / (a * np.sin(gam_rad))
|
|
113
|
+
qx01 = 2 * np.pi / (b * np.sin(gam_rad))
|
|
114
|
+
|
|
115
|
+
# 2. Calculate qxy3 (qx11) using the reciprocal space law of cosines
|
|
116
|
+
qx11 = np.sqrt(qx10**2 + qx01**2 - 2 * qx10 * qx01 * np.cos(gam_rad))
|
|
117
|
+
|
|
118
|
+
# 3. Reverse the azimuth logic
|
|
119
|
+
# azimuth3_deg = azimuth_internal_deg + (gam_deg - 90.0)
|
|
120
|
+
azimuth_internal_deg = azimuth - (gamma - 90.0)
|
|
121
|
+
azimuth_internal_rad = np.deg2rad(azimuth_internal_deg)
|
|
122
|
+
|
|
123
|
+
# 4. Reconstruct the normal vector components
|
|
124
|
+
nz = qx10 * qx01 * np.sin(gam_rad)
|
|
125
|
+
|
|
126
|
+
# Guard against perfectly vertical tilt dividing by zero
|
|
127
|
+
if np.cos(t3_rad) == 0.0:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
nmod = nz / np.cos(t3_rad)
|
|
131
|
+
|
|
132
|
+
# Extract nx and ny using the inverted azimuthal angles
|
|
133
|
+
nxy = np.sqrt(max(0, nmod**2 - nz**2))
|
|
134
|
+
nx = nxy * np.cos(azimuth_internal_rad)
|
|
135
|
+
ny = nxy * np.sin(azimuth_internal_rad)
|
|
136
|
+
|
|
137
|
+
# 5. Reverse the cross product equations to solve for qz10 and qz01
|
|
138
|
+
# nx = -qz10 * qx01 * sin(gamma)
|
|
139
|
+
qz10 = nx / (qx01 * np.sin(gam_rad))
|
|
140
|
+
|
|
141
|
+
# ny = -(qx10 * qz01 + qz10 * qx01 * cos(gamma))
|
|
142
|
+
qz01 = (-ny - qz10 * qx01 * np.cos(gam_rad)) / qx10
|
|
143
|
+
|
|
144
|
+
# 6. Reconstruct qz11 (assuming ideal constructive addition for the 1,1 peak)
|
|
145
|
+
qz11 = qz10 + qz01
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
"qxy1": np.round(qx10, 5),
|
|
149
|
+
"qz1": np.round(qz10, 5),
|
|
150
|
+
"qxy2": np.round(qx01, 5),
|
|
151
|
+
"qz2": np.round(qz01, 5),
|
|
152
|
+
"qxy3": np.round(qx11, 5),
|
|
153
|
+
"qz3": np.round(qz11, 5)
|
|
154
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import h5py
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class D11_SANS:
|
|
7
|
+
|
|
8
|
+
def __init__(self, filename, data=True):
|
|
9
|
+
self.q = np.array([])
|
|
10
|
+
self.I = np.array([])
|
|
11
|
+
self.I_err = np.array([])
|
|
12
|
+
self.q_err = np.array([])
|
|
13
|
+
self.q_fit = np.array([])
|
|
14
|
+
self.I_fit = np.array([])
|
|
15
|
+
self.sample_name = ''
|
|
16
|
+
if data:
|
|
17
|
+
self.load_data(filename)
|
|
18
|
+
else:
|
|
19
|
+
self.load_fit(filename)
|
|
20
|
+
|
|
21
|
+
def __repr__(self):
|
|
22
|
+
return self.sample_name
|
|
23
|
+
|
|
24
|
+
def get_sample_name(self):
|
|
25
|
+
return self.sample_name
|
|
26
|
+
|
|
27
|
+
def load_data(self, filename):
|
|
28
|
+
with open(filename, 'r') as f:
|
|
29
|
+
a = f.read()
|
|
30
|
+
self.sample_name = a.split('Subtitle:')[1].split('\n')[0].replace(' ', '')
|
|
31
|
+
data = np.loadtxt(filename, skiprows=53).T
|
|
32
|
+
self.q = data[0] * 10
|
|
33
|
+
self.I = data[1]
|
|
34
|
+
self.I_err = data[2]
|
|
35
|
+
self.q_err = data[3] * 10
|
|
36
|
+
|
|
37
|
+
def plot(self, ax=None, bckg=0.0, label=None):
|
|
38
|
+
if not ax:
|
|
39
|
+
_fig, ax = plt.subplots(1, 1)
|
|
40
|
+
|
|
41
|
+
if not label:
|
|
42
|
+
label = self.sample_name
|
|
43
|
+
|
|
44
|
+
if isinstance(bckg, float):
|
|
45
|
+
ax.errorbar(self.q, self.I - bckg, yerr=self.I_err, label=label, fmt='.', capsize=3, alpha=0.25)
|
|
46
|
+
else:
|
|
47
|
+
ax.errorbar(self.q, self.I - bckg.I, yerr=self.I_err, label=label, fmt='.', capsize=3, alpha=0.25)
|
|
48
|
+
ax.set_xlabel('$q, \\mathrm{nm}^{-1}$')
|
|
49
|
+
ax.set_ylabel('$\\mathrm{\\Delta}\\Sigma / \\mathrm{\\Delta}\\Omega, \\mathrm{cm}^{-1}$')
|
|
50
|
+
ax.loglog()
|
|
51
|
+
ax.legend()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ID02_SAXS:
|
|
55
|
+
def __init__(self, filename, data=True, sample_thickness=0.2):
|
|
56
|
+
self.q = np.array([])
|
|
57
|
+
self.I = np.array([])
|
|
58
|
+
self.I_err = np.array([])
|
|
59
|
+
self.q_fit = np.array([])
|
|
60
|
+
self.I_fit = np.array([])
|
|
61
|
+
self.sample_name = ''
|
|
62
|
+
self.sample_thickness = sample_thickness # for 2 mm capillary to convert into 1/cm
|
|
63
|
+
if data:
|
|
64
|
+
self.load_data(filename)
|
|
65
|
+
else:
|
|
66
|
+
self.load_fit(filename)
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return self.sample_name
|
|
70
|
+
|
|
71
|
+
def load_fit(self, filename):
|
|
72
|
+
fit = np.loadtxt(filename).T
|
|
73
|
+
# print(fit)
|
|
74
|
+
self.q_fit = fit[0]
|
|
75
|
+
self.I_fit = fit[1]
|
|
76
|
+
|
|
77
|
+
def load_data(self, filename):
|
|
78
|
+
with h5py.File(filename, 'r') as file:
|
|
79
|
+
self.I = np.array(file['entry_0000']['saxsutilities']['data']['I'])[0] / self.sample_thickness
|
|
80
|
+
self.I_err = np.array(file['entry_0000']['saxsutilities']['data']['Idev'])[0] / self.sample_thickness
|
|
81
|
+
self.q = np.array(file['entry_0000']['saxsutilities']['data']['q'])
|
|
82
|
+
self.sample_name = str(file['entry_0000']['saxsutilities']['data']['header_array']['title'][()]).split("\'")[1]
|
|
83
|
+
|
|
84
|
+
def plot(self, ax=None, norm_factor=1, label=None):
|
|
85
|
+
if not ax:
|
|
86
|
+
_fig, ax = plt.subplots(1, 1)
|
|
87
|
+
|
|
88
|
+
if not label:
|
|
89
|
+
label = self.sample_name
|
|
90
|
+
|
|
91
|
+
ax.errorbar(self.q, self.I / norm_factor, self.I_err / norm_factor, fmt='o', capsize=2, linewidth=1,
|
|
92
|
+
markersize=1, label=label)
|
|
93
|
+
ax.set_xlabel('$q, \\mathrm{nm}^{-1}$')
|
|
94
|
+
ax.set_ylabel('$\\mathrm{Intensity, cm}^{-1}$')
|
|
95
|
+
ax.legend()
|
|
96
|
+
ax.loglog()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"id": "initial_id",
|
|
6
|
+
"metadata": {
|
|
7
|
+
"collapsed": true,
|
|
8
|
+
"ExecuteTime": {
|
|
9
|
+
"end_time": "2026-07-29T14:28:04.733134Z",
|
|
10
|
+
"start_time": "2026-07-29T14:28:04.729842Z"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"source": "from src.gid import calculate_cell_parameters, calculate_q_values",
|
|
14
|
+
"outputs": [],
|
|
15
|
+
"execution_count": 6
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"metadata": {
|
|
19
|
+
"ExecuteTime": {
|
|
20
|
+
"end_time": "2026-07-29T14:28:06.207859Z",
|
|
21
|
+
"start_time": "2026-07-29T14:28:06.205370Z"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"cell_type": "code",
|
|
25
|
+
"source": [
|
|
26
|
+
"example_qxy1 = 0.934998\n",
|
|
27
|
+
"example_qxy2 = 1.308997\n",
|
|
28
|
+
"example_qxy3 = 1.379241\n",
|
|
29
|
+
"example_qz1 = 0.431857\n",
|
|
30
|
+
"example_qz2 = 0.604600\n",
|
|
31
|
+
"example_qz3 = 0.172743\n",
|
|
32
|
+
"cell_parameters = calculate_cell_parameters(example_qxy1, example_qxy2, example_qxy3, example_qz1, example_qz2, example_qz3)"
|
|
33
|
+
],
|
|
34
|
+
"id": "8995fbab8e9a5a1c",
|
|
35
|
+
"outputs": [],
|
|
36
|
+
"execution_count": 7
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"metadata": {
|
|
40
|
+
"ExecuteTime": {
|
|
41
|
+
"end_time": "2026-07-29T14:28:06.771160Z",
|
|
42
|
+
"start_time": "2026-07-29T14:28:06.769062Z"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"cell_type": "code",
|
|
46
|
+
"source": [
|
|
47
|
+
"for param in cell_parameters:\n",
|
|
48
|
+
" print(f\"{param} = {cell_parameters[param]}\")"
|
|
49
|
+
],
|
|
50
|
+
"id": "6e7c5042d408d770",
|
|
51
|
+
"outputs": [
|
|
52
|
+
{
|
|
53
|
+
"name": "stdout",
|
|
54
|
+
"output_type": "stream",
|
|
55
|
+
"text": [
|
|
56
|
+
"a = 7.0\n",
|
|
57
|
+
"b = 5.0\n",
|
|
58
|
+
"gamma = 73.74\n",
|
|
59
|
+
"tilt = 30.0\n",
|
|
60
|
+
"azimuth = -53.13\n",
|
|
61
|
+
"area = 33.6\n"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"execution_count": 8
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"metadata": {
|
|
69
|
+
"ExecuteTime": {
|
|
70
|
+
"end_time": "2026-07-29T14:28:07.270147Z",
|
|
71
|
+
"start_time": "2026-07-29T14:28:07.267474Z"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"cell_type": "code",
|
|
75
|
+
"source": "q_vectors = calculate_q_values(7, 5, 73.74, 30, -53.13)",
|
|
76
|
+
"id": "7d152be4ad9aab7b",
|
|
77
|
+
"outputs": [],
|
|
78
|
+
"execution_count": 9
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"metadata": {
|
|
82
|
+
"ExecuteTime": {
|
|
83
|
+
"end_time": "2026-07-29T14:28:07.782791Z",
|
|
84
|
+
"start_time": "2026-07-29T14:28:07.780466Z"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"cell_type": "code",
|
|
88
|
+
"source": [
|
|
89
|
+
"for param in q_vectors:\n",
|
|
90
|
+
" print(f\"{param} = {q_vectors[param]}\")"
|
|
91
|
+
],
|
|
92
|
+
"id": "b7bc54cc671b0c4b",
|
|
93
|
+
"outputs": [
|
|
94
|
+
{
|
|
95
|
+
"name": "stdout",
|
|
96
|
+
"output_type": "stream",
|
|
97
|
+
"text": [
|
|
98
|
+
"qxy1 = 0.935\n",
|
|
99
|
+
"qz1 = 0.43186\n",
|
|
100
|
+
"qxy2 = 1.309\n",
|
|
101
|
+
"qz2 = 0.26603\n",
|
|
102
|
+
"qxy3 = 1.37924\n",
|
|
103
|
+
"qz3 = 0.69788\n"
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"execution_count": 10
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"metadata": {},
|
|
111
|
+
"cell_type": "code",
|
|
112
|
+
"outputs": [],
|
|
113
|
+
"execution_count": null,
|
|
114
|
+
"source": "",
|
|
115
|
+
"id": "7aa839e13949494b"
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
"metadata": {
|
|
119
|
+
"kernelspec": {
|
|
120
|
+
"display_name": "Python 3",
|
|
121
|
+
"language": "python",
|
|
122
|
+
"name": "python3"
|
|
123
|
+
},
|
|
124
|
+
"language_info": {
|
|
125
|
+
"codemirror_mode": {
|
|
126
|
+
"name": "ipython",
|
|
127
|
+
"version": 2
|
|
128
|
+
},
|
|
129
|
+
"file_extension": ".py",
|
|
130
|
+
"mimetype": "text/x-python",
|
|
131
|
+
"name": "python",
|
|
132
|
+
"nbconvert_exporter": "python",
|
|
133
|
+
"pygments_lexer": "ipython2",
|
|
134
|
+
"version": "2.7.6"
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"nbformat": 4,
|
|
138
|
+
"nbformat_minor": 5
|
|
139
|
+
}
|