qrotor 4.0.0a2__tar.gz → 4.0.2__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.
Potentially problematic release.
This version of qrotor might be problematic. Click here for more details.
- {qrotor-4.0.0a2 → qrotor-4.0.2}/PKG-INFO +125 -19
- {qrotor-4.0.0a2 → qrotor-4.0.2}/README.md +123 -18
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/_version.py +1 -1
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/constants.py +25 -12
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/plot.py +7 -4
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/potential.py +30 -14
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor.egg-info/PKG-INFO +125 -19
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor.egg-info/SOURCES.txt +5 -1
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor.egg-info/requires.txt +1 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/setup.py +1 -2
- qrotor-4.0.2/tests/test_constants.py +13 -0
- qrotor-4.0.2/tests/test_potential.py +37 -0
- qrotor-4.0.0a2/tests/test_qrotor.py → qrotor-4.0.2/tests/test_rotate.py +1 -49
- qrotor-4.0.2/tests/test_solve.py +28 -0
- qrotor-4.0.2/tests/test_system.py +24 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/LICENSE +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/__init__.py +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/rotate.py +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/solve.py +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/system.py +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor/systems.py +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor.egg-info/dependency_links.txt +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/qrotor.egg-info/top_level.txt +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/setup.cfg +0 -0
- {qrotor-4.0.0a2 → qrotor-4.0.2}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qrotor
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.2
|
|
4
4
|
Summary: QRotor
|
|
5
5
|
Author: Pablo Gila-Herranz
|
|
6
6
|
Author-email: pgila001@ikasle.ehu.eus
|
|
@@ -21,6 +21,7 @@ Requires-Dist: pandas
|
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: matplotlib
|
|
23
23
|
Requires-Dist: aton
|
|
24
|
+
Requires-Dist: periodictable
|
|
24
25
|
Dynamic: author
|
|
25
26
|
Dynamic: author-email
|
|
26
27
|
Dynamic: classifier
|
|
@@ -33,7 +34,7 @@ Dynamic: requires-dist
|
|
|
33
34
|
Dynamic: requires-python
|
|
34
35
|
Dynamic: summary
|
|
35
36
|
|
|
36
|
-
<p align="center"><img width="
|
|
37
|
+
<p align="center"><img width="60.0%" src="pics/qrotor.png"></p>
|
|
37
38
|
|
|
38
39
|
QRotor is a Python package used to study molecular rotations,
|
|
39
40
|
such as those of methyl and amine groups.
|
|
@@ -44,27 +45,66 @@ These quantum systems are represented by the `qrotor.System()` object.
|
|
|
44
45
|
QRotor can obtain custom potentials from DFT,
|
|
45
46
|
which are used to solve the quantum system.
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Installation
|
|
53
|
+
|
|
54
|
+
As always, it is recommended to install your packages in a virtual environment:
|
|
55
|
+
```bash
|
|
56
|
+
python3 -m venv .venv
|
|
57
|
+
source .venv/bin/activate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## With pip
|
|
62
|
+
|
|
63
|
+
Install or upgrade ATON with
|
|
64
|
+
```bash
|
|
65
|
+
pip install qrotor -U
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## From source
|
|
70
|
+
|
|
71
|
+
Optionally, you can install ATON from the [GitHub repo](https://github.com/pablogila/qrotor/).
|
|
72
|
+
Clone the repository or download the [latest stable release](https://github.com/pablogila/qrotor/tags)
|
|
73
|
+
as a ZIP, unzip it, and run inside it:
|
|
74
|
+
```bash
|
|
75
|
+
pip install .
|
|
76
|
+
```
|
|
48
77
|
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Documentation
|
|
83
|
+
|
|
84
|
+
QRotor contains the following modules:
|
|
51
85
|
|
|
52
86
|
| | |
|
|
53
87
|
| --- | --- |
|
|
88
|
+
| [qrotor.constants](https://pablogila.github.io/qrotor/qrotor/constants.html) | Common bond lengths and inertias |
|
|
54
89
|
| [qrotor.system](https://pablogila.github.io/qrotor/qrotor/system.html) | Definition of the quantum `System` object |
|
|
55
|
-
| [qrotor.systems](https://pablogila.github.io/qrotor/qrotor/systems.html) |
|
|
90
|
+
| [qrotor.systems](https://pablogila.github.io/qrotor/qrotor/systems.html) | Utilities to manage several System objects, such as a list of systems |
|
|
56
91
|
| [qrotor.rotate](https://pablogila.github.io/qrotor/qrotor/rotate.html) | Rotate specific atoms from structural files |
|
|
57
|
-
| [qrotor.constants](https://pablogila.github.io/qrotor/qrotor/constants.html) | Common bond lengths and inertias |
|
|
58
92
|
| [qrotor.potential](https://pablogila.github.io/qrotor/qrotor/potential.html) | Potential definitions and loading functions |
|
|
59
93
|
| [qrotor.solve](https://pablogila.github.io/qrotor/qrotor/solve.html) | Solve rotation eigenvalues and eigenvectors |
|
|
60
|
-
| [qrotor.plot](https://pablogila.github.io/qrotor/qrotor/plot.html) | Plotting
|
|
94
|
+
| [qrotor.plot](https://pablogila.github.io/qrotor/qrotor/plot.html) | Plotting utilities |
|
|
95
|
+
|
|
96
|
+
Check the [full documentation online](https://pablogila.github.io/qrotor/).
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
---
|
|
61
100
|
|
|
62
101
|
|
|
63
102
|
# Usage
|
|
64
103
|
|
|
65
104
|
## Solving quantum rotational systems
|
|
66
105
|
|
|
67
|
-
|
|
106
|
+
Let's start with a basic calculation of the eigenvalues for a zero potential, corresponding to a free rotor.
|
|
107
|
+
A predefined synthetic potential can be used, see all available options in the [qrotor.potential](https://pablogila.github.io/qrotor/qrotor/potential.html) documentation.
|
|
68
108
|
Note that the default energy unit is meV unless stated otherwise.
|
|
69
109
|
|
|
70
110
|
```python
|
|
@@ -74,7 +114,7 @@ system.gridsize = 200000 # Size of the potential grid
|
|
|
74
114
|
system.B = 1 # Rotational inertia
|
|
75
115
|
system.potential_name = 'zero'
|
|
76
116
|
system.solve()
|
|
77
|
-
system.eigenvalues
|
|
117
|
+
print(system.eigenvalues)
|
|
78
118
|
# [0.0, 1.0, 1.0, 4.0, 4.0, 9.0, 9.0, ...] # approx values
|
|
79
119
|
```
|
|
80
120
|
|
|
@@ -87,10 +127,10 @@ in a cosine potential of amplitude 30 meV:
|
|
|
87
127
|
```python
|
|
88
128
|
import qrotor as qr
|
|
89
129
|
system = qr.System()
|
|
90
|
-
system.gridsize = 200000
|
|
130
|
+
system.gridsize = 200000
|
|
91
131
|
system.B = qr.B_CH3 # Rotational inertia of a methyl group
|
|
92
132
|
system.potential_name = 'cosine'
|
|
93
|
-
system.potential_constants = [0, 30, 3, 0] # Offset, max, freq, phase (for
|
|
133
|
+
system.potential_constants = [0, 30, 3, 0] # Offset, max, freq, phase (for cosine potential)
|
|
94
134
|
system.solve()
|
|
95
135
|
# Plot potential and eigenvalues
|
|
96
136
|
qr.plot.energies(system)
|
|
@@ -102,7 +142,7 @@ qr.plot.wavefunction(system, levels=[0,1,2], square=True)
|
|
|
102
142
|
## Custom potentials from DFT
|
|
103
143
|
|
|
104
144
|
QRotor can be used to obtain custom rotational potentials from DFT calculations.
|
|
105
|
-
|
|
145
|
+
To run a Quantum ESPRESSO SCF calculation for a methyl rotation every 10 degrees:
|
|
106
146
|
|
|
107
147
|
```python
|
|
108
148
|
import qrotor as qr
|
|
@@ -121,10 +161,8 @@ api.slurm.sbatch(files=scf_files)
|
|
|
121
161
|
|
|
122
162
|
To load the calculated potential to a QRotor System,
|
|
123
163
|
```python
|
|
124
|
-
# Compile a 'potential.csv' file with the calculated potential as a function of the angle
|
|
125
|
-
qr.potential.from_qe()
|
|
126
|
-
# Load to the system
|
|
127
|
-
system = qr.potential.load()
|
|
164
|
+
# Compile a 'potential.csv' file with the calculated potential as a function of the angle, and load it into a new system
|
|
165
|
+
system = qr.potential.from_qe()
|
|
128
166
|
# Solve the system, interpolating to a bigger gridsize
|
|
129
167
|
system.B = qr.B_CH3
|
|
130
168
|
system.solve(200000)
|
|
@@ -139,9 +177,9 @@ below the potential maximum are also calculated upon solving the system:
|
|
|
139
177
|
|
|
140
178
|
```python
|
|
141
179
|
system.solve()
|
|
142
|
-
system.splittings
|
|
143
|
-
system.excitations
|
|
144
|
-
system.deg
|
|
180
|
+
print(system.splittings)
|
|
181
|
+
print(system.excitations)
|
|
182
|
+
print(system.deg)
|
|
145
183
|
```
|
|
146
184
|
|
|
147
185
|
An integer `System.deg` degeneracy (e.g. 3 for methyls)
|
|
@@ -165,3 +203,71 @@ See [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.
|
|
|
165
203
|
and [A. J. Horsewill, Progress in Nuclear Magnetic Resonance Spectroscopy 35, 359–389 (1999)](https://doi.org/10.1016/S0079-6565(99)00016-3)
|
|
166
204
|
for further reference.
|
|
167
205
|
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Contributing
|
|
211
|
+
|
|
212
|
+
If you are interested in opening an issue or a pull request, please feel free to do so on [GitHub](https://github.com/pablogila/qrotor/).
|
|
213
|
+
For major changes, please get in touch first to discuss the details.
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
## Code style
|
|
217
|
+
|
|
218
|
+
Please try to follow some general guidelines:
|
|
219
|
+
- Use a code style consistent with the rest of the project.
|
|
220
|
+
- Include docstrings to document new additions.
|
|
221
|
+
- Include automated tests for new features or modifications, see [automated testing](#automated-testing).
|
|
222
|
+
- Arrange function arguments by order of relevance.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
## Automated testing
|
|
226
|
+
|
|
227
|
+
If you are modifying the source code, you should run the automated tests of the [`tests/`](https://github.com/pablogila/qrotor/tree/main/tests) folder to check that everything works as intended.
|
|
228
|
+
To do so, first install PyTest in your environment,
|
|
229
|
+
```bash
|
|
230
|
+
pip install pytest
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
And then run PyTest inside the main directory,
|
|
234
|
+
```bash
|
|
235
|
+
pytest -vv
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
## Compiling the documentation
|
|
240
|
+
|
|
241
|
+
The documentation can be compiled automatically to `docs/qrotor.html` with [Pdoc](https://pdoc.dev/) and [ATON](https://pablogila.github.io/aton), by running:
|
|
242
|
+
```shell
|
|
243
|
+
python3 makedocs.py
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
This runs Pdoc, updating links and pictures, and using the custom theme CSS template from the `css/` folder.
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# Citation
|
|
253
|
+
|
|
254
|
+
QRotor is currently under development.
|
|
255
|
+
Please cite it if you use it in your research,
|
|
256
|
+
> Pablo Gila-Herranz, *QRotor*, https://pablogila.github.io/qrotor
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# License
|
|
263
|
+
|
|
264
|
+
Copyright (C) 2025 Pablo Gila-Herranz
|
|
265
|
+
This program is free software: you can redistribute it and/or modify
|
|
266
|
+
it under the terms of the **GNU Affero General Public License** as published
|
|
267
|
+
by the Free Software Foundation, either version **3** of the License, or
|
|
268
|
+
(at your option) any later version.
|
|
269
|
+
This program is distributed in the hope that it will be useful,
|
|
270
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
271
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
272
|
+
See the attached GNU Affero General Public License for more details.
|
|
273
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<p align="center"><img width="
|
|
1
|
+
<p align="center"><img width="60.0%" src="pics/qrotor.png"></p>
|
|
2
2
|
|
|
3
3
|
QRotor is a Python package used to study molecular rotations,
|
|
4
4
|
such as those of methyl and amine groups.
|
|
@@ -9,27 +9,66 @@ These quantum systems are represented by the `qrotor.System()` object.
|
|
|
9
9
|
QRotor can obtain custom potentials from DFT,
|
|
10
10
|
which are used to solve the quantum system.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Installation
|
|
17
|
+
|
|
18
|
+
As always, it is recommended to install your packages in a virtual environment:
|
|
19
|
+
```bash
|
|
20
|
+
python3 -m venv .venv
|
|
21
|
+
source .venv/bin/activate
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## With pip
|
|
26
|
+
|
|
27
|
+
Install or upgrade ATON with
|
|
28
|
+
```bash
|
|
29
|
+
pip install qrotor -U
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## From source
|
|
34
|
+
|
|
35
|
+
Optionally, you can install ATON from the [GitHub repo](https://github.com/pablogila/qrotor/).
|
|
36
|
+
Clone the repository or download the [latest stable release](https://github.com/pablogila/qrotor/tags)
|
|
37
|
+
as a ZIP, unzip it, and run inside it:
|
|
38
|
+
```bash
|
|
39
|
+
pip install .
|
|
40
|
+
```
|
|
13
41
|
|
|
14
42
|
|
|
15
|
-
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Documentation
|
|
47
|
+
|
|
48
|
+
QRotor contains the following modules:
|
|
16
49
|
|
|
17
50
|
| | |
|
|
18
51
|
| --- | --- |
|
|
52
|
+
| [qrotor.constants](https://pablogila.github.io/qrotor/qrotor/constants.html) | Common bond lengths and inertias |
|
|
19
53
|
| [qrotor.system](https://pablogila.github.io/qrotor/qrotor/system.html) | Definition of the quantum `System` object |
|
|
20
|
-
| [qrotor.systems](https://pablogila.github.io/qrotor/qrotor/systems.html) |
|
|
54
|
+
| [qrotor.systems](https://pablogila.github.io/qrotor/qrotor/systems.html) | Utilities to manage several System objects, such as a list of systems |
|
|
21
55
|
| [qrotor.rotate](https://pablogila.github.io/qrotor/qrotor/rotate.html) | Rotate specific atoms from structural files |
|
|
22
|
-
| [qrotor.constants](https://pablogila.github.io/qrotor/qrotor/constants.html) | Common bond lengths and inertias |
|
|
23
56
|
| [qrotor.potential](https://pablogila.github.io/qrotor/qrotor/potential.html) | Potential definitions and loading functions |
|
|
24
57
|
| [qrotor.solve](https://pablogila.github.io/qrotor/qrotor/solve.html) | Solve rotation eigenvalues and eigenvectors |
|
|
25
|
-
| [qrotor.plot](https://pablogila.github.io/qrotor/qrotor/plot.html) | Plotting
|
|
58
|
+
| [qrotor.plot](https://pablogila.github.io/qrotor/qrotor/plot.html) | Plotting utilities |
|
|
59
|
+
|
|
60
|
+
Check the [full documentation online](https://pablogila.github.io/qrotor/).
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
---
|
|
26
64
|
|
|
27
65
|
|
|
28
66
|
# Usage
|
|
29
67
|
|
|
30
68
|
## Solving quantum rotational systems
|
|
31
69
|
|
|
32
|
-
|
|
70
|
+
Let's start with a basic calculation of the eigenvalues for a zero potential, corresponding to a free rotor.
|
|
71
|
+
A predefined synthetic potential can be used, see all available options in the [qrotor.potential](https://pablogila.github.io/qrotor/qrotor/potential.html) documentation.
|
|
33
72
|
Note that the default energy unit is meV unless stated otherwise.
|
|
34
73
|
|
|
35
74
|
```python
|
|
@@ -39,7 +78,7 @@ system.gridsize = 200000 # Size of the potential grid
|
|
|
39
78
|
system.B = 1 # Rotational inertia
|
|
40
79
|
system.potential_name = 'zero'
|
|
41
80
|
system.solve()
|
|
42
|
-
system.eigenvalues
|
|
81
|
+
print(system.eigenvalues)
|
|
43
82
|
# [0.0, 1.0, 1.0, 4.0, 4.0, 9.0, 9.0, ...] # approx values
|
|
44
83
|
```
|
|
45
84
|
|
|
@@ -52,10 +91,10 @@ in a cosine potential of amplitude 30 meV:
|
|
|
52
91
|
```python
|
|
53
92
|
import qrotor as qr
|
|
54
93
|
system = qr.System()
|
|
55
|
-
system.gridsize = 200000
|
|
94
|
+
system.gridsize = 200000
|
|
56
95
|
system.B = qr.B_CH3 # Rotational inertia of a methyl group
|
|
57
96
|
system.potential_name = 'cosine'
|
|
58
|
-
system.potential_constants = [0, 30, 3, 0] # Offset, max, freq, phase (for
|
|
97
|
+
system.potential_constants = [0, 30, 3, 0] # Offset, max, freq, phase (for cosine potential)
|
|
59
98
|
system.solve()
|
|
60
99
|
# Plot potential and eigenvalues
|
|
61
100
|
qr.plot.energies(system)
|
|
@@ -67,7 +106,7 @@ qr.plot.wavefunction(system, levels=[0,1,2], square=True)
|
|
|
67
106
|
## Custom potentials from DFT
|
|
68
107
|
|
|
69
108
|
QRotor can be used to obtain custom rotational potentials from DFT calculations.
|
|
70
|
-
|
|
109
|
+
To run a Quantum ESPRESSO SCF calculation for a methyl rotation every 10 degrees:
|
|
71
110
|
|
|
72
111
|
```python
|
|
73
112
|
import qrotor as qr
|
|
@@ -86,10 +125,8 @@ api.slurm.sbatch(files=scf_files)
|
|
|
86
125
|
|
|
87
126
|
To load the calculated potential to a QRotor System,
|
|
88
127
|
```python
|
|
89
|
-
# Compile a 'potential.csv' file with the calculated potential as a function of the angle
|
|
90
|
-
qr.potential.from_qe()
|
|
91
|
-
# Load to the system
|
|
92
|
-
system = qr.potential.load()
|
|
128
|
+
# Compile a 'potential.csv' file with the calculated potential as a function of the angle, and load it into a new system
|
|
129
|
+
system = qr.potential.from_qe()
|
|
93
130
|
# Solve the system, interpolating to a bigger gridsize
|
|
94
131
|
system.B = qr.B_CH3
|
|
95
132
|
system.solve(200000)
|
|
@@ -104,9 +141,9 @@ below the potential maximum are also calculated upon solving the system:
|
|
|
104
141
|
|
|
105
142
|
```python
|
|
106
143
|
system.solve()
|
|
107
|
-
system.splittings
|
|
108
|
-
system.excitations
|
|
109
|
-
system.deg
|
|
144
|
+
print(system.splittings)
|
|
145
|
+
print(system.excitations)
|
|
146
|
+
print(system.deg)
|
|
110
147
|
```
|
|
111
148
|
|
|
112
149
|
An integer `System.deg` degeneracy (e.g. 3 for methyls)
|
|
@@ -130,3 +167,71 @@ See [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.
|
|
|
130
167
|
and [A. J. Horsewill, Progress in Nuclear Magnetic Resonance Spectroscopy 35, 359–389 (1999)](https://doi.org/10.1016/S0079-6565(99)00016-3)
|
|
131
168
|
for further reference.
|
|
132
169
|
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# Contributing
|
|
175
|
+
|
|
176
|
+
If you are interested in opening an issue or a pull request, please feel free to do so on [GitHub](https://github.com/pablogila/qrotor/).
|
|
177
|
+
For major changes, please get in touch first to discuss the details.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
## Code style
|
|
181
|
+
|
|
182
|
+
Please try to follow some general guidelines:
|
|
183
|
+
- Use a code style consistent with the rest of the project.
|
|
184
|
+
- Include docstrings to document new additions.
|
|
185
|
+
- Include automated tests for new features or modifications, see [automated testing](#automated-testing).
|
|
186
|
+
- Arrange function arguments by order of relevance.
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
## Automated testing
|
|
190
|
+
|
|
191
|
+
If you are modifying the source code, you should run the automated tests of the [`tests/`](https://github.com/pablogila/qrotor/tree/main/tests) folder to check that everything works as intended.
|
|
192
|
+
To do so, first install PyTest in your environment,
|
|
193
|
+
```bash
|
|
194
|
+
pip install pytest
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
And then run PyTest inside the main directory,
|
|
198
|
+
```bash
|
|
199
|
+
pytest -vv
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
## Compiling the documentation
|
|
204
|
+
|
|
205
|
+
The documentation can be compiled automatically to `docs/qrotor.html` with [Pdoc](https://pdoc.dev/) and [ATON](https://pablogila.github.io/aton), by running:
|
|
206
|
+
```shell
|
|
207
|
+
python3 makedocs.py
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
This runs Pdoc, updating links and pictures, and using the custom theme CSS template from the `css/` folder.
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# Citation
|
|
217
|
+
|
|
218
|
+
QRotor is currently under development.
|
|
219
|
+
Please cite it if you use it in your research,
|
|
220
|
+
> Pablo Gila-Herranz, *QRotor*, https://pablogila.github.io/qrotor
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# License
|
|
227
|
+
|
|
228
|
+
Copyright (C) 2025 Pablo Gila-Herranz
|
|
229
|
+
This program is free software: you can redistribute it and/or modify
|
|
230
|
+
it under the terms of the **GNU Affero General Public License** as published
|
|
231
|
+
by the Free Software Foundation, either version **3** of the License, or
|
|
232
|
+
(at your option) any later version.
|
|
233
|
+
This program is distributed in the hope that it will be useful,
|
|
234
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
235
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
236
|
+
See the attached GNU Affero General Public License for more details.
|
|
237
|
+
|
|
@@ -11,7 +11,8 @@ Bond lengths and angles were obtained from MAPbI3, see
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
|
-
import
|
|
14
|
+
import periodictable
|
|
15
|
+
import scipy.constants as const
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
# Distance between Carbon and Hydrogen atoms (measured from MAPbI3)
|
|
@@ -31,29 +32,31 @@ angle_NH = 180 - angle_NH_external
|
|
|
31
32
|
"""Internal angle of the X-N-H bond, in degrees."""
|
|
32
33
|
|
|
33
34
|
# Rotation radius (calculated from distance and angle)
|
|
34
|
-
r_CH = distance_CH * np.sin(np.deg2rad(angle_CH)) *
|
|
35
|
+
r_CH = distance_CH * np.sin(np.deg2rad(angle_CH)) * 1e-10
|
|
35
36
|
"""Rotation radius of the methyl group, in meters."""
|
|
36
|
-
r_NH = distance_NH * np.sin(np.deg2rad(angle_NH)) *
|
|
37
|
+
r_NH = distance_NH * np.sin(np.deg2rad(angle_NH)) * 1e-10
|
|
37
38
|
"""Rotation radius of the amine group, in meters."""
|
|
38
39
|
|
|
39
40
|
# Inertia, SI units
|
|
40
|
-
|
|
41
|
+
_amu = const.physical_constants['atomic mass constant'][0]
|
|
42
|
+
I_CH3 = 3 * (periodictable.H.mass * _amu * r_CH**2)
|
|
41
43
|
"""Inertia of CH3, in kg·m^2."""
|
|
42
|
-
I_CD3 = 3 * (
|
|
44
|
+
I_CD3 = 3 * (periodictable.D.mass * _amu * r_CH**2)
|
|
43
45
|
"""Inertia of CD3, in kg·m^2."""
|
|
44
|
-
I_NH3 = 3 * (
|
|
46
|
+
I_NH3 = 3 * (periodictable.H.mass * _amu * r_NH**2)
|
|
45
47
|
"""Inertia of NH3, in kg·m^2."""
|
|
46
|
-
I_ND3 = 3 * (
|
|
48
|
+
I_ND3 = 3 * (periodictable.D.mass * _amu * r_NH**2)
|
|
47
49
|
"""Inertia of ND3, in kg·m^2."""
|
|
48
50
|
|
|
49
|
-
# Rotational energy
|
|
50
|
-
|
|
51
|
+
# Rotational energy
|
|
52
|
+
_hbar = const.physical_constants['reduced Planck constant'][0]
|
|
53
|
+
B_CH3 = ((_hbar**2) / (2 * I_CH3)) * (1000 / const.eV)
|
|
51
54
|
"""Rotational energy of CH3, in meV·s/kg·m^2."""
|
|
52
|
-
B_CD3 = ((
|
|
55
|
+
B_CD3 = ((_hbar**2) / (2 * I_CD3)) * (1000 / const.eV)
|
|
53
56
|
"""Rotational energy of CD3, in meV·s/kg·m^2."""
|
|
54
|
-
B_NH3 = ((
|
|
57
|
+
B_NH3 = ((_hbar**2) / (2 * I_NH3)) * (1000 / const.eV)
|
|
55
58
|
"""Rotational energy of NH3, in meV·s/kg·m^2."""
|
|
56
|
-
B_ND3 = ((
|
|
59
|
+
B_ND3 = ((_hbar**2) / (2 * I_ND3)) * (1000 / const.eV)
|
|
57
60
|
"""Rotational energy of ND3, in meV·s/kg·m^2."""
|
|
58
61
|
|
|
59
62
|
# Potential constants from titov2023 [C1, C2, C3, C4, C5]
|
|
@@ -70,3 +73,13 @@ for the `qrotor.potential.titov2023` potential.
|
|
|
70
73
|
In meV units.
|
|
71
74
|
"""
|
|
72
75
|
|
|
76
|
+
# Quick conversion factors
|
|
77
|
+
Ry_to_eV = const.physical_constants['Rydberg constant times hc in eV'][0]
|
|
78
|
+
"""Quick conversion factor from eV to Rydberg energy."""
|
|
79
|
+
Ry_to_meV = Ry_to_eV * 1000
|
|
80
|
+
"""Quick conversion factor from meV to Rydberg energy."""
|
|
81
|
+
eV_to_Ry = 1 / Ry_to_eV
|
|
82
|
+
"""Quick conversion factor from Rydberg to eV."""
|
|
83
|
+
meV_to_Ry = 1 / Ry_to_meV
|
|
84
|
+
"""Quick conversion factor from Rydberg to meV."""
|
|
85
|
+
|
|
@@ -21,11 +21,11 @@ This module provides straightforward functions to plot QRotor data.
|
|
|
21
21
|
|
|
22
22
|
from .system import System
|
|
23
23
|
from . import systems
|
|
24
|
+
from . import constants
|
|
24
25
|
import matplotlib.pyplot as plt
|
|
25
26
|
import numpy as np
|
|
26
27
|
from copy import deepcopy
|
|
27
28
|
import aton.alias as alias
|
|
28
|
-
import aton.phys as phys
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def potential(
|
|
@@ -271,7 +271,7 @@ def splittings(
|
|
|
271
271
|
The different `System.comment` are shown in the horizontal axis.
|
|
272
272
|
An optional `title` can be specified.
|
|
273
273
|
Default units shown are $\\mu$eV (`'ueV'`).
|
|
274
|
-
Available units are: `'ueV'`, `'meV'`, `'Ry'
|
|
274
|
+
Available units are: `'ueV'`, `'meV'`, `'Ry'`, or `'B'` (free rotor units).
|
|
275
275
|
"""
|
|
276
276
|
title = title if title != None else 'Tunnel splitting energies'
|
|
277
277
|
calcs = deepcopy(data)
|
|
@@ -284,11 +284,14 @@ def splittings(
|
|
|
284
284
|
x = [c.comment for c in calcs]
|
|
285
285
|
# What units do we want?
|
|
286
286
|
if units.lower() in alias.units['ueV']:
|
|
287
|
-
y = [j *
|
|
287
|
+
y = [j * 1000 for j in y] # Convert meV to micro eV
|
|
288
288
|
ax.set_ylabel("Energy / $\\mu$eV")
|
|
289
289
|
elif units.lower() in alias.units['Ry']:
|
|
290
|
-
y = [j *
|
|
290
|
+
y = [j * constants.meV_to_Ry for j in y]
|
|
291
291
|
ax.set_ylabel("Energy / Ry")
|
|
292
|
+
elif units.upper() == 'B':
|
|
293
|
+
y = [j / c.B for j, c in zip(y, calcs)]
|
|
294
|
+
ax.set_ylabel("Energy / B")
|
|
292
295
|
#else: # It's okay let's use meV
|
|
293
296
|
|
|
294
297
|
ax.bar(range(len(y)), y)
|
|
@@ -50,7 +50,6 @@ from scipy.interpolate import CubicSpline
|
|
|
50
50
|
import aton.alias as alias
|
|
51
51
|
import aton.file as file
|
|
52
52
|
import aton.api.qe as qe
|
|
53
|
-
import aton.phys as phys
|
|
54
53
|
from ._version import __version__
|
|
55
54
|
|
|
56
55
|
|
|
@@ -67,6 +66,7 @@ def save(
|
|
|
67
66
|
in degrees and meVs by default.
|
|
68
67
|
The units can be changed with `angle` and `energy`,
|
|
69
68
|
but only change these defaults if you know what you are doing.
|
|
69
|
+
An optional `comment` can be included in the header of the file.
|
|
70
70
|
"""
|
|
71
71
|
print('Saving potential data file...')
|
|
72
72
|
# Check if a previous potential.dat file exists, and ask to overwrite it
|
|
@@ -78,7 +78,7 @@ def save(
|
|
|
78
78
|
print("Aborted.")
|
|
79
79
|
return None
|
|
80
80
|
# Set header
|
|
81
|
-
potential_data = f'
|
|
81
|
+
potential_data = f'## {comment}\n' if comment else f'## {system.comment}\n' if system.comment else ''
|
|
82
82
|
potential_data += '# Rotational potential dataset\n'
|
|
83
83
|
potential_data += f'# Saved with QRotor {__version__}\n'
|
|
84
84
|
potential_data += '# https://pablogila.github.io/qrotor\n'
|
|
@@ -100,10 +100,10 @@ def save(
|
|
|
100
100
|
if energy.lower() in alias.units['meV']:
|
|
101
101
|
potential_data += 'Potential/meV\n'
|
|
102
102
|
elif energy.lower() in alias.units['eV']:
|
|
103
|
-
potential_values = potential_values *
|
|
103
|
+
potential_values = potential_values * 1e-3
|
|
104
104
|
potential_data += 'Potential/eV\n'
|
|
105
105
|
elif energy.lower() in alias.units['Ry']:
|
|
106
|
-
potential_values = potential_values *
|
|
106
|
+
potential_values = potential_values * constants.meV_to_Ry
|
|
107
107
|
potential_data += 'Potential/Ry\n'
|
|
108
108
|
else:
|
|
109
109
|
print(f"WARNING: Unrecognised '{energy}' energy units, using meV instead")
|
|
@@ -144,6 +144,11 @@ def load(
|
|
|
144
144
|
system = System() if system is None else system
|
|
145
145
|
with open(file_path, 'r') as f:
|
|
146
146
|
lines = f.readlines()
|
|
147
|
+
# Read the comment
|
|
148
|
+
loaded_comment = ''
|
|
149
|
+
if lines[0].startswith('## '):
|
|
150
|
+
loaded_comment = lines[0][3:].strip()
|
|
151
|
+
# Read data
|
|
147
152
|
positions = []
|
|
148
153
|
potentials = []
|
|
149
154
|
for line in lines:
|
|
@@ -161,19 +166,24 @@ def load(
|
|
|
161
166
|
raise ValueError(f"Angle unit '{angle}' not recognized.")
|
|
162
167
|
# Save energies to numpy arrays
|
|
163
168
|
if energy.lower() in alias.units['eV']:
|
|
164
|
-
potentials = np.array(potentials) *
|
|
169
|
+
potentials = np.array(potentials) * 1000
|
|
165
170
|
elif energy.lower() in alias.units['meV']:
|
|
166
171
|
potentials = np.array(potentials)
|
|
167
172
|
elif energy.lower() in alias.units['Ry']:
|
|
168
|
-
potentials = np.array(potentials) *
|
|
173
|
+
potentials = np.array(potentials) * constants.Ry_to_meV
|
|
169
174
|
else:
|
|
170
175
|
raise ValueError(f"Energy unit '{energy}' not recognized.")
|
|
171
176
|
# Set the system
|
|
172
177
|
system.grid = np.array(positions)
|
|
173
178
|
system.gridsize = len(positions)
|
|
174
179
|
system.potential_values = np.array(potentials)
|
|
175
|
-
# System comment as the parent folder name
|
|
176
|
-
|
|
180
|
+
# System comment as the loaded comment or the parent folder name
|
|
181
|
+
if comment:
|
|
182
|
+
system.comment = comment
|
|
183
|
+
elif loaded_comment:
|
|
184
|
+
system.comment = loaded_comment
|
|
185
|
+
else:
|
|
186
|
+
system.comment = os.path.basename(os.path.dirname(file_path))
|
|
177
187
|
print(f"Loaded {filepath}")
|
|
178
188
|
return system
|
|
179
189
|
|
|
@@ -185,9 +195,10 @@ def from_qe(
|
|
|
185
195
|
exclude:list=['slurm-'],
|
|
186
196
|
energy:str='meV',
|
|
187
197
|
comment:str=None,
|
|
188
|
-
) ->
|
|
198
|
+
) -> System:
|
|
189
199
|
"""Compiles a rotational potential CSV file from Quantum ESPRESSO outputs,
|
|
190
200
|
created with `qrotor.rotate.structure_qe()`.
|
|
201
|
+
Returns a `System` object with the new potential values.
|
|
191
202
|
|
|
192
203
|
The angle in degrees is extracted from the output filenames,
|
|
193
204
|
which must follow `whatever_ANGLE.out`.
|
|
@@ -214,7 +225,7 @@ def from_qe(
|
|
|
214
225
|
files = file.get_list(folder=folder, include=include, exclude=exclude, abspath=True)
|
|
215
226
|
folder_name = os.path.basename(folder)
|
|
216
227
|
# Set header
|
|
217
|
-
potential_data = f'
|
|
228
|
+
potential_data = f'## {comment}\n' if comment else f'## {folder_name}\n'
|
|
218
229
|
potential_data += '# Rotational potential dataset\n'
|
|
219
230
|
potential_data += f'# Calculated with QE using QRotor {__version__}\n'
|
|
220
231
|
potential_data += '# https://pablogila.github.io/qrotor\n'
|
|
@@ -244,15 +255,15 @@ def from_qe(
|
|
|
244
255
|
counter_errors += 1
|
|
245
256
|
continue
|
|
246
257
|
if energy.lower() in alias.units['eV']:
|
|
247
|
-
energy_value = content['Energy'] *
|
|
258
|
+
energy_value = content['Energy'] * constants.Ry_to_eV
|
|
248
259
|
elif energy.lower() in alias.units['meV']:
|
|
249
|
-
energy_value = content['Energy'] *
|
|
260
|
+
energy_value = content['Energy'] * constants.Ry_to_meV
|
|
250
261
|
elif energy.lower() in alias.units['Ry']:
|
|
251
262
|
energy_value = content['Energy']
|
|
252
263
|
else:
|
|
253
264
|
print(f"WARNING: Energy unit '{energy}' not recognized, using meV instead.")
|
|
254
265
|
energy = 'meV'
|
|
255
|
-
energy_value = content['Energy'] *
|
|
266
|
+
energy_value = content['Energy'] * constants.Ry_to_meV
|
|
256
267
|
splits = filename.split('_')
|
|
257
268
|
angle_value = splits[-1].replace('.out', '')
|
|
258
269
|
angle_value = float(angle_value)
|
|
@@ -274,7 +285,12 @@ def from_qe(
|
|
|
274
285
|
# Warn the user if not in default units
|
|
275
286
|
if energy.lower() not in alias.units['meV']:
|
|
276
287
|
print(f"WARNING: You saved the potential in '{energy}' units! Remember that QRotor works in meVs!")
|
|
277
|
-
|
|
288
|
+
new_system = None
|
|
289
|
+
try:
|
|
290
|
+
new_system = load(filepath=filepath, comment=comment, energy=energy)
|
|
291
|
+
except:
|
|
292
|
+
pass
|
|
293
|
+
return new_system
|
|
278
294
|
|
|
279
295
|
|
|
280
296
|
def merge(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qrotor
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.2
|
|
4
4
|
Summary: QRotor
|
|
5
5
|
Author: Pablo Gila-Herranz
|
|
6
6
|
Author-email: pgila001@ikasle.ehu.eus
|
|
@@ -21,6 +21,7 @@ Requires-Dist: pandas
|
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: matplotlib
|
|
23
23
|
Requires-Dist: aton
|
|
24
|
+
Requires-Dist: periodictable
|
|
24
25
|
Dynamic: author
|
|
25
26
|
Dynamic: author-email
|
|
26
27
|
Dynamic: classifier
|
|
@@ -33,7 +34,7 @@ Dynamic: requires-dist
|
|
|
33
34
|
Dynamic: requires-python
|
|
34
35
|
Dynamic: summary
|
|
35
36
|
|
|
36
|
-
<p align="center"><img width="
|
|
37
|
+
<p align="center"><img width="60.0%" src="pics/qrotor.png"></p>
|
|
37
38
|
|
|
38
39
|
QRotor is a Python package used to study molecular rotations,
|
|
39
40
|
such as those of methyl and amine groups.
|
|
@@ -44,27 +45,66 @@ These quantum systems are represented by the `qrotor.System()` object.
|
|
|
44
45
|
QRotor can obtain custom potentials from DFT,
|
|
45
46
|
which are used to solve the quantum system.
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Installation
|
|
53
|
+
|
|
54
|
+
As always, it is recommended to install your packages in a virtual environment:
|
|
55
|
+
```bash
|
|
56
|
+
python3 -m venv .venv
|
|
57
|
+
source .venv/bin/activate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## With pip
|
|
62
|
+
|
|
63
|
+
Install or upgrade ATON with
|
|
64
|
+
```bash
|
|
65
|
+
pip install qrotor -U
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## From source
|
|
70
|
+
|
|
71
|
+
Optionally, you can install ATON from the [GitHub repo](https://github.com/pablogila/qrotor/).
|
|
72
|
+
Clone the repository or download the [latest stable release](https://github.com/pablogila/qrotor/tags)
|
|
73
|
+
as a ZIP, unzip it, and run inside it:
|
|
74
|
+
```bash
|
|
75
|
+
pip install .
|
|
76
|
+
```
|
|
48
77
|
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Documentation
|
|
83
|
+
|
|
84
|
+
QRotor contains the following modules:
|
|
51
85
|
|
|
52
86
|
| | |
|
|
53
87
|
| --- | --- |
|
|
88
|
+
| [qrotor.constants](https://pablogila.github.io/qrotor/qrotor/constants.html) | Common bond lengths and inertias |
|
|
54
89
|
| [qrotor.system](https://pablogila.github.io/qrotor/qrotor/system.html) | Definition of the quantum `System` object |
|
|
55
|
-
| [qrotor.systems](https://pablogila.github.io/qrotor/qrotor/systems.html) |
|
|
90
|
+
| [qrotor.systems](https://pablogila.github.io/qrotor/qrotor/systems.html) | Utilities to manage several System objects, such as a list of systems |
|
|
56
91
|
| [qrotor.rotate](https://pablogila.github.io/qrotor/qrotor/rotate.html) | Rotate specific atoms from structural files |
|
|
57
|
-
| [qrotor.constants](https://pablogila.github.io/qrotor/qrotor/constants.html) | Common bond lengths and inertias |
|
|
58
92
|
| [qrotor.potential](https://pablogila.github.io/qrotor/qrotor/potential.html) | Potential definitions and loading functions |
|
|
59
93
|
| [qrotor.solve](https://pablogila.github.io/qrotor/qrotor/solve.html) | Solve rotation eigenvalues and eigenvectors |
|
|
60
|
-
| [qrotor.plot](https://pablogila.github.io/qrotor/qrotor/plot.html) | Plotting
|
|
94
|
+
| [qrotor.plot](https://pablogila.github.io/qrotor/qrotor/plot.html) | Plotting utilities |
|
|
95
|
+
|
|
96
|
+
Check the [full documentation online](https://pablogila.github.io/qrotor/).
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
---
|
|
61
100
|
|
|
62
101
|
|
|
63
102
|
# Usage
|
|
64
103
|
|
|
65
104
|
## Solving quantum rotational systems
|
|
66
105
|
|
|
67
|
-
|
|
106
|
+
Let's start with a basic calculation of the eigenvalues for a zero potential, corresponding to a free rotor.
|
|
107
|
+
A predefined synthetic potential can be used, see all available options in the [qrotor.potential](https://pablogila.github.io/qrotor/qrotor/potential.html) documentation.
|
|
68
108
|
Note that the default energy unit is meV unless stated otherwise.
|
|
69
109
|
|
|
70
110
|
```python
|
|
@@ -74,7 +114,7 @@ system.gridsize = 200000 # Size of the potential grid
|
|
|
74
114
|
system.B = 1 # Rotational inertia
|
|
75
115
|
system.potential_name = 'zero'
|
|
76
116
|
system.solve()
|
|
77
|
-
system.eigenvalues
|
|
117
|
+
print(system.eigenvalues)
|
|
78
118
|
# [0.0, 1.0, 1.0, 4.0, 4.0, 9.0, 9.0, ...] # approx values
|
|
79
119
|
```
|
|
80
120
|
|
|
@@ -87,10 +127,10 @@ in a cosine potential of amplitude 30 meV:
|
|
|
87
127
|
```python
|
|
88
128
|
import qrotor as qr
|
|
89
129
|
system = qr.System()
|
|
90
|
-
system.gridsize = 200000
|
|
130
|
+
system.gridsize = 200000
|
|
91
131
|
system.B = qr.B_CH3 # Rotational inertia of a methyl group
|
|
92
132
|
system.potential_name = 'cosine'
|
|
93
|
-
system.potential_constants = [0, 30, 3, 0] # Offset, max, freq, phase (for
|
|
133
|
+
system.potential_constants = [0, 30, 3, 0] # Offset, max, freq, phase (for cosine potential)
|
|
94
134
|
system.solve()
|
|
95
135
|
# Plot potential and eigenvalues
|
|
96
136
|
qr.plot.energies(system)
|
|
@@ -102,7 +142,7 @@ qr.plot.wavefunction(system, levels=[0,1,2], square=True)
|
|
|
102
142
|
## Custom potentials from DFT
|
|
103
143
|
|
|
104
144
|
QRotor can be used to obtain custom rotational potentials from DFT calculations.
|
|
105
|
-
|
|
145
|
+
To run a Quantum ESPRESSO SCF calculation for a methyl rotation every 10 degrees:
|
|
106
146
|
|
|
107
147
|
```python
|
|
108
148
|
import qrotor as qr
|
|
@@ -121,10 +161,8 @@ api.slurm.sbatch(files=scf_files)
|
|
|
121
161
|
|
|
122
162
|
To load the calculated potential to a QRotor System,
|
|
123
163
|
```python
|
|
124
|
-
# Compile a 'potential.csv' file with the calculated potential as a function of the angle
|
|
125
|
-
qr.potential.from_qe()
|
|
126
|
-
# Load to the system
|
|
127
|
-
system = qr.potential.load()
|
|
164
|
+
# Compile a 'potential.csv' file with the calculated potential as a function of the angle, and load it into a new system
|
|
165
|
+
system = qr.potential.from_qe()
|
|
128
166
|
# Solve the system, interpolating to a bigger gridsize
|
|
129
167
|
system.B = qr.B_CH3
|
|
130
168
|
system.solve(200000)
|
|
@@ -139,9 +177,9 @@ below the potential maximum are also calculated upon solving the system:
|
|
|
139
177
|
|
|
140
178
|
```python
|
|
141
179
|
system.solve()
|
|
142
|
-
system.splittings
|
|
143
|
-
system.excitations
|
|
144
|
-
system.deg
|
|
180
|
+
print(system.splittings)
|
|
181
|
+
print(system.excitations)
|
|
182
|
+
print(system.deg)
|
|
145
183
|
```
|
|
146
184
|
|
|
147
185
|
An integer `System.deg` degeneracy (e.g. 3 for methyls)
|
|
@@ -165,3 +203,71 @@ See [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.
|
|
|
165
203
|
and [A. J. Horsewill, Progress in Nuclear Magnetic Resonance Spectroscopy 35, 359–389 (1999)](https://doi.org/10.1016/S0079-6565(99)00016-3)
|
|
166
204
|
for further reference.
|
|
167
205
|
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Contributing
|
|
211
|
+
|
|
212
|
+
If you are interested in opening an issue or a pull request, please feel free to do so on [GitHub](https://github.com/pablogila/qrotor/).
|
|
213
|
+
For major changes, please get in touch first to discuss the details.
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
## Code style
|
|
217
|
+
|
|
218
|
+
Please try to follow some general guidelines:
|
|
219
|
+
- Use a code style consistent with the rest of the project.
|
|
220
|
+
- Include docstrings to document new additions.
|
|
221
|
+
- Include automated tests for new features or modifications, see [automated testing](#automated-testing).
|
|
222
|
+
- Arrange function arguments by order of relevance.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
## Automated testing
|
|
226
|
+
|
|
227
|
+
If you are modifying the source code, you should run the automated tests of the [`tests/`](https://github.com/pablogila/qrotor/tree/main/tests) folder to check that everything works as intended.
|
|
228
|
+
To do so, first install PyTest in your environment,
|
|
229
|
+
```bash
|
|
230
|
+
pip install pytest
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
And then run PyTest inside the main directory,
|
|
234
|
+
```bash
|
|
235
|
+
pytest -vv
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
## Compiling the documentation
|
|
240
|
+
|
|
241
|
+
The documentation can be compiled automatically to `docs/qrotor.html` with [Pdoc](https://pdoc.dev/) and [ATON](https://pablogila.github.io/aton), by running:
|
|
242
|
+
```shell
|
|
243
|
+
python3 makedocs.py
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
This runs Pdoc, updating links and pictures, and using the custom theme CSS template from the `css/` folder.
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# Citation
|
|
253
|
+
|
|
254
|
+
QRotor is currently under development.
|
|
255
|
+
Please cite it if you use it in your research,
|
|
256
|
+
> Pablo Gila-Herranz, *QRotor*, https://pablogila.github.io/qrotor
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# License
|
|
263
|
+
|
|
264
|
+
Copyright (C) 2025 Pablo Gila-Herranz
|
|
265
|
+
This program is free software: you can redistribute it and/or modify
|
|
266
|
+
it under the terms of the **GNU Affero General Public License** as published
|
|
267
|
+
by the Free Software Foundation, either version **3** of the License, or
|
|
268
|
+
(at your option) any later version.
|
|
269
|
+
This program is distributed in the hope that it will be useful,
|
|
270
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
271
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
272
|
+
See the attached GNU Affero General Public License for more details.
|
|
273
|
+
|
|
@@ -16,4 +16,8 @@ qrotor.egg-info/dependency_links.txt
|
|
|
16
16
|
qrotor.egg-info/requires.txt
|
|
17
17
|
qrotor.egg-info/top_level.txt
|
|
18
18
|
tests/__init__.py
|
|
19
|
-
tests/
|
|
19
|
+
tests/test_constants.py
|
|
20
|
+
tests/test_potential.py
|
|
21
|
+
tests/test_rotate.py
|
|
22
|
+
tests/test_solve.py
|
|
23
|
+
tests/test_system.py
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
3
|
DESCRIPTION = "QRotor"
|
|
4
|
-
LONG_DESCRIPTION = "QRotor"
|
|
5
4
|
|
|
6
5
|
exec(open('qrotor/_version.py').read())
|
|
7
6
|
|
|
@@ -17,7 +16,7 @@ setup(
|
|
|
17
16
|
long_description = LONG_DESCRIPTION,
|
|
18
17
|
long_description_content_type = 'text/markdown',
|
|
19
18
|
packages = find_packages(),
|
|
20
|
-
install_requires = ['scipy', 'pandas', 'numpy', 'matplotlib', 'aton'],
|
|
19
|
+
install_requires = ['scipy', 'pandas', 'numpy', 'matplotlib', 'aton', 'periodictable'],
|
|
21
20
|
extras_requires = {'dev': ['pytest', 'twine', 'build']},
|
|
22
21
|
python_requires = '>=3',
|
|
23
22
|
license = 'AGPL-3.0',
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import qrotor as qr
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_constants():
|
|
5
|
+
assert round(qr.B_CH3, 5) == 0.64518
|
|
6
|
+
assert round(qr.B_CD3, 5) == 0.32289
|
|
7
|
+
assert round(qr.B_NH3, 5) == 0.73569
|
|
8
|
+
assert round(qr.B_ND3, 5) == 0.36819
|
|
9
|
+
assert round(qr.Ry_to_eV, 5) == 13.60569
|
|
10
|
+
assert round(qr.Ry_to_meV, 5) == 13605.69312
|
|
11
|
+
assert round(qr.eV_to_Ry, 5) == 0.07350
|
|
12
|
+
assert round(qr.meV_to_Ry, 10) == .0000734986
|
|
13
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import qrotor as qr
|
|
2
|
+
import aton
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
folder = 'tests/samples/'
|
|
6
|
+
structure = folder + 'CH3NH3.in'
|
|
7
|
+
structure_120 = folder + 'CH3NH3_120.in'
|
|
8
|
+
structure_60 = folder + 'CH3NH3_60.in'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_save_and_load():
|
|
12
|
+
system = qr.System()
|
|
13
|
+
system.gridsize = 36
|
|
14
|
+
system.potential_name = 'sin'
|
|
15
|
+
system.B = 1
|
|
16
|
+
system.solve_potential()
|
|
17
|
+
potential_file = folder + '_temp_potential.csv'
|
|
18
|
+
# Remove the file if it exists
|
|
19
|
+
try:
|
|
20
|
+
aton.file.remove(potential_file)
|
|
21
|
+
except:
|
|
22
|
+
pass
|
|
23
|
+
qr.potential.save(system, comment='hi', filepath=potential_file)
|
|
24
|
+
system_new = qr.potential.load(potential_file)
|
|
25
|
+
assert system_new.gridsize == system.gridsize
|
|
26
|
+
assert round(system_new.potential_values[0], 5) == round(system.potential_values[0], 5)
|
|
27
|
+
assert round(system_new.potential_values[5], 5) == round(system.potential_values[5], 5)
|
|
28
|
+
assert round(system_new.potential_values[13], 5) == round(system.potential_values[13], 5)
|
|
29
|
+
assert system_new.comment == 'hi'
|
|
30
|
+
aton.file.remove(potential_file)
|
|
31
|
+
# If we don't provide a comment, it should be the name of the folder
|
|
32
|
+
system.comment = None
|
|
33
|
+
qr.potential.save(system, filepath=potential_file)
|
|
34
|
+
system_new = qr.potential.load(potential_file)
|
|
35
|
+
assert system_new.comment == 'samples'
|
|
36
|
+
aton.file.remove(potential_file)
|
|
37
|
+
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import qrotor as qr
|
|
2
2
|
import aton.api as api
|
|
3
3
|
import aton.txt.extract as extract
|
|
4
4
|
import aton.file as file
|
|
5
|
-
import numpy as np
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
folder = 'tests/samples/'
|
|
@@ -52,50 +51,3 @@ def test_rotate():
|
|
|
52
51
|
assert coord_rounded == rotated_coord_rounded
|
|
53
52
|
file.remove(structure_60)
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
def test_solve_zero():
|
|
57
|
-
system = qr.System()
|
|
58
|
-
system.gridsize = 50000
|
|
59
|
-
system.potential_name = 'zero'
|
|
60
|
-
system.B = 1
|
|
61
|
-
system.solve()
|
|
62
|
-
assert round(system.eigenvalues[0], 2) == 0.0
|
|
63
|
-
assert round(system.eigenvalues[1], 2) == 1.0
|
|
64
|
-
assert round(system.eigenvalues[2], 2) == 1.0
|
|
65
|
-
assert round(system.eigenvalues[3], 2) == 4.0
|
|
66
|
-
assert round(system.eigenvalues[4], 2) == 4.0
|
|
67
|
-
assert round(system.eigenvalues[5], 2) == 9.0
|
|
68
|
-
assert round(system.eigenvalues[6], 2) == 9.0
|
|
69
|
-
assert round(system.eigenvalues[7], 2) == 16.0
|
|
70
|
-
assert round(system.eigenvalues[8], 2) == 16.0
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def test_solve_potential():
|
|
74
|
-
system = qr.System()
|
|
75
|
-
system.gridsize = 500
|
|
76
|
-
system.potential_name = 'sin'
|
|
77
|
-
system.potential_constants = [0, 1, 3, 0]
|
|
78
|
-
system.solve_potential()
|
|
79
|
-
assert round(system.potential_max, 2) == 1.0
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def test_phase():
|
|
83
|
-
sys = qr.System()
|
|
84
|
-
sys.B = 1.0
|
|
85
|
-
sys.potential_name = 'cos'
|
|
86
|
-
sys.gridsize = 10000
|
|
87
|
-
sys.solve()
|
|
88
|
-
# plus pi/2, which will be -3pi/2
|
|
89
|
-
sys.change_phase(0.5)
|
|
90
|
-
assert round(sys.grid[0], 2) == round(-np.pi * 3/2, 2)
|
|
91
|
-
# The first potential value should be 0,
|
|
92
|
-
# but remember that the potential offset is corrected
|
|
93
|
-
# so it should be half potential_max, so 1.0/2
|
|
94
|
-
assert round(sys.potential_values[0], 2) == 0.5
|
|
95
|
-
# minus pi, which will become -pi/2
|
|
96
|
-
sys.change_phase(-1)
|
|
97
|
-
assert round(sys.grid[0], 2) == round(-np.pi/2, 2)
|
|
98
|
-
assert round(sys.potential_values[0], 2) == 0.5
|
|
99
|
-
# Were eigenvalues calculated?
|
|
100
|
-
assert len(sys.eigenvalues) > 0
|
|
101
|
-
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import qrotor as qr
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_solve_zero():
|
|
5
|
+
system = qr.System()
|
|
6
|
+
system.gridsize = 50000
|
|
7
|
+
system.potential_name = 'zero'
|
|
8
|
+
system.B = 1
|
|
9
|
+
system.solve()
|
|
10
|
+
assert round(system.eigenvalues[0], 2) == 0.0
|
|
11
|
+
assert round(system.eigenvalues[1], 2) == 1.0
|
|
12
|
+
assert round(system.eigenvalues[2], 2) == 1.0
|
|
13
|
+
assert round(system.eigenvalues[3], 2) == 4.0
|
|
14
|
+
assert round(system.eigenvalues[4], 2) == 4.0
|
|
15
|
+
assert round(system.eigenvalues[5], 2) == 9.0
|
|
16
|
+
assert round(system.eigenvalues[6], 2) == 9.0
|
|
17
|
+
assert round(system.eigenvalues[7], 2) == 16.0
|
|
18
|
+
assert round(system.eigenvalues[8], 2) == 16.0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_solve_potential():
|
|
22
|
+
system = qr.System()
|
|
23
|
+
system.gridsize = 500
|
|
24
|
+
system.potential_name = 'sin'
|
|
25
|
+
system.potential_constants = [0, 1, 3, 0]
|
|
26
|
+
system.solve_potential()
|
|
27
|
+
assert round(system.potential_max, 2) == 1.0
|
|
28
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import qrotor as qr
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_phase():
|
|
6
|
+
sys = qr.System()
|
|
7
|
+
sys.B = 1.0
|
|
8
|
+
sys.potential_name = 'cos'
|
|
9
|
+
sys.gridsize = 10000
|
|
10
|
+
sys.solve()
|
|
11
|
+
# plus pi/2, which will be -3pi/2
|
|
12
|
+
sys.change_phase(0.5)
|
|
13
|
+
assert round(sys.grid[0], 2) == round(-np.pi * 3/2, 2)
|
|
14
|
+
# The first potential value should be 0,
|
|
15
|
+
# but remember that the potential offset is corrected
|
|
16
|
+
# so it should be half potential_max, so 1.0/2
|
|
17
|
+
assert round(sys.potential_values[0], 2) == 0.5
|
|
18
|
+
# minus pi, which will become -pi/2
|
|
19
|
+
sys.change_phase(-1)
|
|
20
|
+
assert round(sys.grid[0], 2) == round(-np.pi/2, 2)
|
|
21
|
+
assert round(sys.potential_values[0], 2) == 0.5
|
|
22
|
+
# Were eigenvalues calculated?
|
|
23
|
+
assert len(sys.eigenvalues) > 0
|
|
24
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|