aton 0.0.1rc5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
aton/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ """
2
+ .. include:: ../_README_temp.md
3
+ """
4
+
5
+
6
+ from ._version import __version__ as version
7
+ from . import st
8
+ from . import phys
9
+ from . import text
10
+ from . import interface
11
+ from . import spx
12
+
aton/_version.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ # Description
3
+
4
+ Package version is defined here. Follows semantic versioning, as in:
5
+
6
+ `vMAJOR.MINOR.PATCH.`
7
+
8
+ More about semantic versioning:
9
+ https://semver.org/
10
+
11
+ """
12
+
13
+ __version__ = 'v0.0.1rc5'
14
+
@@ -0,0 +1,76 @@
1
+ """
2
+ # *Ab-initio* interfaces
3
+
4
+ This module contains interfaces for several *ab-initio* calculation softwares.
5
+ These interfaces can be easily expanded with the `aton.text` module.
6
+
7
+
8
+ # Index
9
+
10
+ | | |
11
+ | --- | --- |
12
+ | `aton.interface.qe` | Interface for [Quantum ESPRESSO](https://www.quantum-espresso.org/)'s [pw.x](https://www.quantum-espresso.org/Doc/INPUT_PW.html) module |
13
+ | `aton.interface.phonopy` | Interface for [Phonopy](https://phonopy.github.io/phonopy/) calculations |
14
+ | `aton.interface.castep` | Interface for [CASTEP](https://castep-docs.github.io/castep-docs/) calculations |
15
+
16
+
17
+ # Examples
18
+
19
+ ## Quantum ESPRESSO
20
+
21
+ To read the output from a Quantum ESPRESSO pw.x calculation,
22
+ ```python
23
+ from aton.interface import qe
24
+ # Read to a dictionary
25
+ calculation = qe.read_out('relax.out')
26
+ calculation.keys() # See the available values
27
+ # Final energy from the calculation
28
+ energy = calculation['Energy']
29
+ ```
30
+
31
+ To modify values from an input file,
32
+ ```python
33
+ from aton.interface import qe
34
+ # Add a hydrogen atom to a specific position
35
+ qe.add_atom('H 0.10 0.20 0.30')
36
+ # Set the input ecutwfc value
37
+ qe.set_value('relax.in', 'ecutwfc', 60.0)
38
+ ```
39
+
40
+ Check the full `aton.interface.qe` API reference for more details.
41
+
42
+
43
+ ## Phonopy
44
+
45
+ To perform a phonon calculation from a relaxed structure via Quantum ESPRESSO,
46
+ ```python
47
+ from aton import interface
48
+ # Create the supercell inputs
49
+ interface.phonopy.make(dimension='2 2 2', folder='./calculation')
50
+ # Sbatch to a cluster
51
+ interface.phonopy.sbatch('./calculation')
52
+ ```
53
+
54
+ Check the full `aton.interface.phonopy` API reference for more details.
55
+
56
+
57
+ ## CASTEP
58
+
59
+ To read output values from a CASTEP calculation,
60
+ ```python
61
+ from aton.interface import castep
62
+ # Read the output
63
+ output = castep.read_castep('calculation.castep')
64
+ # Get the final energy
65
+ energy = output['Energy']
66
+ ```
67
+
68
+ Check the full `aton.interface.castep` API reference for more details.
69
+
70
+ """
71
+
72
+
73
+ from . import qe
74
+ from . import phonopy
75
+ from . import castep
76
+
@@ -0,0 +1,93 @@
1
+ """
2
+ # Description
3
+
4
+ Functions to work with [CASTEP](https://castep-docs.github.io/castep-docs/) calculation files.
5
+
6
+ # Index
7
+
8
+ | | |
9
+ | --- | --- |
10
+ | `read_castep()` | Output reading |
11
+
12
+ ---
13
+ """
14
+
15
+
16
+ import aton.st.file as file
17
+ import aton.text.find as find
18
+ import aton.text.extract as extract
19
+
20
+
21
+ def read_castep(filename) -> dict:
22
+ """
23
+ Reads a CASTEP output file, specified in `filename`.
24
+ Returns a dictionary with the following keys:
25
+ `'Enthalpy'` (LBFGS: Final Enthalpy, in kJ/mol),
26
+ `'Energy'` (Total energy corrected for finite basis set, in eV),
27
+ `'Space group'`, `'Volume'` (Angstrom^3), `'Density'` (amu/Angstrom^3), `'Density_g'` (g/cm^3),
28
+ `'A'`, `'B'`, `'C'` (Angstroms), `'Alpha'`, `'Beta'`, `'Gamma'` (Degrees).\n
29
+ Note that these output keys start with a **C**apital letter.
30
+ """
31
+ file_castep = file.get(filename)
32
+ # Initial definitions
33
+ enthalpy = None
34
+ energy = None
35
+ space_group = None
36
+ volume = None
37
+ density = None
38
+ density_g = None
39
+ a = None
40
+ b = None
41
+ c = None
42
+ alpha = None
43
+ beta = None
44
+ gamma = None
45
+ # Find the output values in the file
46
+ enthalpy_str = find.lines(file_castep, 'LBFGS: Final Enthalpy =', -1)
47
+ energy_str = find.lines(file_castep, 'Total energy corrected for finite basis set =', -1)
48
+ space_group_str = find.lines(file_castep, 'Space group of crystal =', -1)
49
+ volume_str = find.lines(file_castep, 'Current cell volume =', -1)
50
+ density_str = find.lines(file_castep, 'density =', -1, 1)
51
+ a_str = find.lines(file_castep, 'a =', -1)
52
+ b_str = find.lines(file_castep, 'b =', -1)
53
+ c_str = find.lines(file_castep, 'c =', -1)
54
+
55
+ if enthalpy_str:
56
+ enthalpy = extract.number(enthalpy_str[0], 'LBFGS: Final Enthalpy')
57
+ if energy_str:
58
+ energy = extract.number(energy_str[0], 'Total energy corrected for finite basis set')
59
+ if space_group_str:
60
+ # Avoid little stupid errors
61
+ space_group_str = space_group_str.replace(',','.')
62
+ space_group = extract.string(space_group_str[0], 'Space group of crystal')
63
+ if volume_str:
64
+ volume = extract.number(volume_str[0], 'Current cell volume')
65
+ if density_str:
66
+ density = extract.number(density_str[0], 'density')
67
+ density_g = extract.number(density_str[1], '')
68
+ if a:
69
+ a = extract.number(a_str, 'a')
70
+ alpha = extract.number(a_str, 'alpha')
71
+ if b:
72
+ b = extract.number(b_str, 'b')
73
+ beta = extract.number(b_str, 'beta')
74
+ if c:
75
+ c = extract.number(c_str, 'c')
76
+ gamma = extract.number(c_str, 'gamma')
77
+ # Return as a dict
78
+ dictionary = {
79
+ 'Enthalpy' : enthalpy,
80
+ 'Energy' : energy,
81
+ 'Space group' : space_group,
82
+ 'Volume' : volume,
83
+ 'Density' : density,
84
+ 'Density_g' : density_g,
85
+ 'A' : a,
86
+ 'B' : b,
87
+ 'C' : c,
88
+ 'Alpha' : alpha,
89
+ 'Beta' : beta,
90
+ 'Gamma' : gamma,
91
+ }
92
+ return dictionary
93
+
@@ -0,0 +1,267 @@
1
+ """
2
+ # Description
3
+
4
+ Functions to work with [Phonopy](https://phonopy.github.io/phonopy/) calculations,
5
+ along with [Quantum ESPRESSO](https://www.quantum-espresso.org/).
6
+
7
+
8
+ # Index
9
+
10
+ Create supercell inputs and sbatch phonon calculations
11
+ | | |
12
+ | --- | --- |
13
+ | `make()` | Build the supercell inputs |
14
+ | `sbatch()` | Sbatch'es the supercell calculations |
15
+
16
+ For more control
17
+ `supercells_from_scf()`
18
+ `scf_header_to_supercells()`
19
+ `check_slurm_template()`
20
+
21
+ ---
22
+ """
23
+
24
+
25
+ import os
26
+ import re
27
+ from aton._version import __version__
28
+ import aton.st.file as file
29
+ import aton.st.call as call
30
+ import aton.text.find as find
31
+ import aton.text.edit as edit # text
32
+ import aton.text.extract as extract
33
+ import aton.interface.qe as qe
34
+
35
+
36
+ def make(
37
+ dimension:str='2 2 2',
38
+ folder:str=None,
39
+ relax_in:str='relax.in',
40
+ relax_out:str='relax.out',
41
+ slurm_template:str='scf.slurm'
42
+ ) -> None:
43
+ '''
44
+ Starting on a given `folder` (CWD if none) from the `relax_in` and `relax_out` (default ones),
45
+ creates the supercells of a `dimension` (`2 2 2` by default)
46
+ needed for the Phonopy calculations with Quantum ESPRESSO.
47
+ It runs sequentially `thotpy.qe.scf_from_relax()`, `supercells_from_scf()` and `scf_header_to_supercells()`.
48
+ Finally, it checks the `slurm_template` with `check_slurm_template()`.
49
+ '''
50
+ print(f'\nWelcome to thotpy.phonopy {__version__}\n'
51
+ 'Creating all supercell inputs with Phonopy for Quantum ESPRESSO...\n')
52
+ qe.scf_from_relax(folder, relax_in, relax_out)
53
+ supercells_from_scf(dimension, folder)
54
+ scf_header_to_supercells(folder)
55
+ print('\n------------------------------------------------------\n'
56
+ 'PLEASE CHECH BELOW THE CONTENT OF THE supercell-001.in\n'
57
+ '------------------------------------------------------\n')
58
+ call.bash('head -n 100 supercell-001.in')
59
+ print('\n------------------------------------------------------\n'
60
+ 'PLEASE CHECH THE CONTENT OF THE supercell-001.in\n'
61
+ 'The first 100 lines of the input were printed above!\n'
62
+ '------------------------------------------------------\n\n'
63
+ 'If it seems correct, run the calculations with thotpy.phonopy.sbatch()\n')
64
+ check_slurm_template(folder, slurm_template)
65
+ return None
66
+
67
+
68
+ def sbatch(
69
+ folder=None,
70
+ slurm_template:str='scf.slurm',
71
+ testing:bool=False
72
+ ) -> None:
73
+ '''
74
+ Launch all your supercell calculations to a cluster using a SLURM manager.
75
+ Runs from a `folder` (CWD if empty), using a `slurm_template` (`scf.slurm` by default).\n
76
+ If `testing=True` it skips the final sbatching, just printing the commands on the screen.\n
77
+ The slurm template must contain the keywords
78
+ `INPUT_FILE`, `OUTPUT_FILE`, and `JOB_NAME` in the following lines:
79
+ ```
80
+ #SBATCH --job-name=JOB_NAME
81
+ mpirun pw.x -inp INPUT_FILE > OUTPUT_FILE
82
+ ```
83
+ '''
84
+ print(f'\naton.interface.phonopy {__version__}\n'
85
+ 'Sbatching all supercells...\n')
86
+ key_input = 'INPUT_FILE'
87
+ key_output = 'OUTPUT_FILE'
88
+ key_jobname = 'JOB_NAME'
89
+ id_pattern = re.compile(r'supercell-(\d\d\d).in')
90
+ slurm_folder = 'slurms'
91
+ folder = call.here(folder)
92
+ # Get supercells and abort if not found
93
+ supercells = file.get_list(folder, 'supercell-')
94
+ if len(supercells) == 0:
95
+ raise FileNotFoundError('Supercells were not found! Did you run thotpy.phonopy.make() ?')
96
+ call.bash(f"mkdir {slurm_folder}", folder, True, True)
97
+ # Get the template
98
+ slurm_file = check_slurm_template(folder, slurm_template)
99
+ if not slurm_file:
100
+ print(f'Aborting... Please correct {slurm_template}\n')
101
+ return None
102
+ for supercell in supercells:
103
+ # Get the file ID
104
+ match = re.search(id_pattern, supercell)
105
+ calc_id = match.group(1)
106
+ # Create slurm file for this supercell
107
+ slurm_id = 'scf-' + calc_id + '.slurm'
108
+ supercell = os.path.basename(supercell)
109
+ supercell_out = supercell.replace('.in', '.out')
110
+ fixing_dict = {
111
+ key_jobname: calc_id,
112
+ key_input: supercell,
113
+ key_output: supercell_out
114
+ }
115
+ edit.from_template(slurm_file, slurm_id, fixing_dict)
116
+ if testing:
117
+ call.bash(f"echo {slurm_id}", folder)
118
+ else:
119
+ call.bash(f"sbatch {slurm_id}", folder, True, False)
120
+ call.bash(f"mv {slurm_id} {slurm_folder}", folder, False, True) # Do not raise error if we can't move the file
121
+ print(f'\nDone! Temporary slurm files were moved to /{slurm_folder}/\n')
122
+
123
+
124
+ def supercells_from_scf(
125
+ dimension:str='2 2 2',
126
+ folder:str=None,
127
+ scf:str='scf.in'
128
+ ) -> None:
129
+ '''
130
+ Creates supercells of a given `dimension` (`2 2 2` by default) inside a `folder`,
131
+ from a Quantum ESPRESSO `scf` input (`scf.in` by default).
132
+ '''
133
+ print(f'\naton.interface.phonopy {__version__}\n')
134
+ folder = call.here(folder)
135
+ scf_in = file.get(folder, scf, True)
136
+ if scf_in is None:
137
+ raise ValueError('No SCF input found in path!')
138
+ call.bash(f'phonopy --qe -d --dim="{dimension}" -c {scf_in}')
139
+ return None
140
+
141
+
142
+ def scf_header_to_supercells(
143
+ folder:str=None,
144
+ scf:str='scf.in',
145
+ ) -> None:
146
+ '''
147
+ Paste the header from the `scf` file in `folder` to the supercells created by Phonopy.
148
+ '''
149
+ print(f'\naton.interface.phonopy {__version__}\n'
150
+ f'Adding headers to Phonopy supercells for Quantum ESPRESSO...\n')
151
+ folder = call.here(folder)
152
+ # Check if the header file, the scf.in, exists
153
+ scf_file = file.get(folder, scf, True)
154
+ if scf_file is None:
155
+ raise ValueError('No header file found in path!')
156
+ # Check if the supercells exist
157
+ supercells = file.get_list(folder, 'supercell-')
158
+ if supercells is None:
159
+ raise ValueError('No supercells found in path!')
160
+ # Check if the supercells contains '&CONTROL' and abort if so
161
+ supercell_sample = supercells[0]
162
+ is_control = find.lines(supercell_sample, r'(&CONTROL|&control)', 1, 0, False, True)
163
+ if is_control:
164
+ raise ValueError('Supercells already contain &CONTROL! Did you do this already?')
165
+ # Check if the keyword is in the scf file
166
+ is_header = find.lines(scf_file, r'ATOMIC_SPECIES', 1, 0, False, False)
167
+ if not is_header:
168
+ raise ValueError('No ATOMIC_SPECIES found in header!')
169
+ # Copy the scf to a temp file
170
+ temp_scf = '_scf_temp.in'
171
+ file.copy(scf_file, temp_scf)
172
+ # Remove the top content from the temp file
173
+ edit.delete_under(temp_scf, 'K_POINTS', -1, 2, False)
174
+ # Find the new number of atoms and replace the line
175
+ updated_values = find.lines(supercell_sample, 'ibrav', 1) # ! ibrav = 0, nat = 384, ntyp = 5
176
+ if not updated_values:
177
+ print("!!! Okay listen, this is weird. This code should never be running, "
178
+ "but for some reson we couldn't find the updated values in the supercells. "
179
+ "Please, introduce the NEW NUMBER OF ATOMS in the supercells manually (int):")
180
+ nat = int(input('nat = '))
181
+ else:
182
+ nat = extract.number(updated_values[0], 'nat')
183
+ qe.set_value(temp_scf, 'nat', nat)
184
+ # Remove the lattice parameters, since Phonopy already indicates units
185
+ qe.set_value(temp_scf, 'celldm(1)', '')
186
+ qe.set_value(temp_scf, 'A', '')
187
+ qe.set_value(temp_scf, 'B', '')
188
+ qe.set_value(temp_scf, 'C', '')
189
+ qe.set_value(temp_scf, 'cosAB', '')
190
+ qe.set_value(temp_scf, 'cosAC', '')
191
+ qe.set_value(temp_scf, 'cosBC', '')
192
+ # Add the header to the supercells
193
+ with open(temp_scf, 'r') as f:
194
+ header = f.read()
195
+ for supercell in supercells:
196
+ edit.insert_at(supercell, header, 0)
197
+ # Remove the temp file
198
+ os.remove('_scf_temp.in')
199
+ print('Done!')
200
+ return None
201
+
202
+
203
+ def check_slurm_template(
204
+ folder=None,
205
+ slurm_template:str='scf.slurm'
206
+ ) -> str:
207
+ '''
208
+ Check a `slurm_template` inside `folder`.
209
+ The current working directory is used if `folder` is not provided.
210
+ If the file does not exist or is invalid, creates a `scf_EXAMPLE.slurm` file for reference.
211
+ '''
212
+ folder = call.here(folder)
213
+ slurm_example = 'scf_EXAMPLE.slurm'
214
+ new_slurm_file = os.path.join(folder, slurm_example)
215
+ # Default slurm template
216
+ content =f'''# Automatic slurm template created with aton.interface.phonopy {__version__}. https://github.com/pablogila/ThotPy
217
+ #!/bin/bash
218
+ #SBATCH --partition=general
219
+ #SBATCH --qos=regular
220
+ #SBATCH --job-name=JOB_NAME
221
+ #SBATCH --ntasks=32
222
+ #SBATCH --time=1-00:00:00
223
+ #SBATCH --mem=128G
224
+ # #SBATCH --mail-user=YOUR@EMAIL
225
+ # #SBATCH --mail-type=END
226
+
227
+ module purge
228
+ module load QuantumESPRESSO/7.3-foss-2023a
229
+
230
+ mpirun pw.x -inp INPUT_FILE > OUTPUT_FILE
231
+ '''
232
+ # If the slurm template does not exist, create one
233
+ slurm_file = file.get(folder, slurm_template, True)
234
+ if not slurm_file:
235
+ with open(new_slurm_file, 'w') as f:
236
+ f.write(content)
237
+ print(f'!!! WARNING: Slurm template missing, so an example was generated automatically:\n'
238
+ f'{slurm_example}\n'
239
+ f'PLEASE CHECK it, UPDATE it and RENAME it to {slurm_template}\n'
240
+ 'before running thotpy.phonopy.sbatch()\n')
241
+ return None
242
+ # Check that the slurm file contains the INPUT_FILE, OUTPUT_FILE and JOB_NAME keywords
243
+ key_input = find.lines(slurm_file, 'INPUT_FILE')
244
+ key_output = find.lines(slurm_file, 'OUTPUT_FILE')
245
+ key_jobname = find.lines(slurm_file, 'JOB_NAME')
246
+ missing = []
247
+ if not key_input:
248
+ missing.append('INPUT_FILE')
249
+ if not key_output:
250
+ missing.append('OUTPUT_FILE')
251
+ if not key_jobname:
252
+ missing.append('JOB_NAME')
253
+ if len(missing) > 0:
254
+ with open(new_slurm_file, 'w') as f:
255
+ f.write(content)
256
+ print('!!! WARNING: Some keywords were missing from your slurm template,\n'
257
+ f'PLEASE CHECK the example at {slurm_example}\n'
258
+ 'before running thotpy.phonopy.sbatch()\n'
259
+ f'The following keywords were missing from your {slurm_template}:')
260
+ for key in missing:
261
+ print(key)
262
+ print('')
263
+ return None
264
+ print(f"Your slurm template {slurm_template} SEEMS OKAY, "
265
+ "but don't forget to check it before running thotpy.phonopy.sbatch()\n")
266
+ return slurm_file # Ready to use!
267
+