Skip to content

Commit 6fb89b3

Browse files
Create Tutorial.md
This is a tutorial made for the CCEM group members about using the TBLite software for performing xTB calculations. 
1 parent f4144fd commit 6fb89b3

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

python/tblite/Tutorial.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Quick Introduction to TBLite - a Practical Guide
2+
3+
## Installation
4+
Under a conda environment, install tblite and tblite-python through the conda-forge channel.
5+
`conda install -c conda-forge tblite`
6+
`conda install -c conda-forge tblite-python`
7+
Make sure you also have ASE installed as well as the basic scientific programming packages.
8+
9+
## Python API and Usage in ASE
10+
### Single-Point Calculations
11+
The skeleton of the script is very simple: the calculator is initialised, and attached to your atom object. Then, most functionality of ASE is readily available.
12+
```python
13+
from tblite.ase import TBLite
14+
from ase.build import fcc111, molecule, add_adsorbate
15+
16+
# Create or import your atoms object
17+
slab = fcc111('Ni', size=(2,2,3))
18+
add_adsorbate(slab, 'H', height=1.5, 'ontop')
19+
slab.center(vacuum=10.0, axis=2)
20+
21+
# Set up calculator
22+
calc = TBLite(method="GFN1-xTB")
23+
24+
# Attach to atoms object
25+
slab.calc = calc
26+
slab.get_potential_energy()
27+
```
28+
29+
### Structure Relaxation
30+
In order to perform a relaxation, we need an external optimizer that allows for convergence.
31+
ASE has several optimizers that can be used: https://wiki.fysik.dtu.dk/ase/ase/optimize.html
32+
However, the following are recommended:
33+
- Fast Internal Relaxation Engine: FIRE
34+
- Non-linear (Polak-Ribiere) conjugate gradient algorithm: SciPyFminCG
35+
- Broyden–Fletcher–Goldfarb–Shanno algorithm: BFGS
36+
37+
Let's carry on with the previous example:
38+
```python
39+
# Import optimizer from ASE
40+
from ase.optimize.sciopt import SciPyFminCG
41+
42+
# Set up optimizer and run
43+
optimizer = SciPyFminCG(slab, trajectory='path/to/directory/system_relaxation.traj')
44+
optimizer.run(fmax=0.05)
45+
```
46+
47+
#### Trajectory Files
48+
A trajectory file will be created in the specifier directory. This file contains all the ionic steps and can be visualised with ase gui. Each frame of this "movie" is an atoms object that can be imported as a vasp file.
49+
50+
First, let's start by storing the optimized structure:
51+
```python
52+
from ase.io import write, read
53+
54+
# Write the relaxed CO2 structure to a VASP file
55+
write('path/to/directory/system_relaxed.vasp', slab, format='vasp')
56+
```
57+
We can also read/write specific frames:
58+
```python
59+
# Extract specific frames
60+
frames = read('path/to/directory/system_relaxation.traj', index='0,3,5')
61+
62+
# Write extracted frames as individual VASP files
63+
for i, atoms in enumerate(frames):
64+
write(f'path/to/directory/frame_{i}.vasp', atoms, format='vasp')
65+
```
66+
In the command line:
67+
- Specify images to load: ase gui filename.traj -i 0,3,5
68+
- A continuous range of frames: ase gui filename.traj -i 1:5
69+
70+
### Calculation of Vibrational Modes - Entropic Correction
71+
After the `optimizer.run(fmax=0.05)` line has completed, the ASE atoms object will be in its optimized state. Calling `get_potential_energy()` on it will return the total energy of this optimized state.
72+
```python
73+
latest_total_energy = slab.get_potential_energy()
74+
print("Latest total energy:", latest_total_energy, "eV")
75+
```
76+
TBLite provides the electronic free energy (that is why we set an electronic temperature). We can compute the nuclear contribution associated with the vibration degrees of freedom with TBlite. For a better explanation of such entropic contribution, check out the Notion page.
77+
```python
78+
from ase.vibrations import Vibrations
79+
from ase.thermochemistry import HarmonicThermo
80+
81+
vib = Vibrations(slab, name='path/to/directory/CO2_vibrations') # Specify the name or path where you want the data saved
82+
83+
# Calculate vibrations
84+
vib.run()
85+
86+
# Once the calculations are done, you can get the vibrational frequencies/energies, etc
87+
frequencies = vib.get_frequencies()
88+
energies = vib.get_energies()
89+
90+
# Calculate the Helmholtz energy for each hydrogen and store it in the list
91+
thermo_properties = HarmonicThermo(vib_energies=energies)
92+
F = thermo_properties.get_helmholtz_energy(temperature=298.0)
93+
print("Helmholtz Energy:", F, "eV")
94+
95+
# Total final energy:
96+
print("Total final energy:", latest_total_energy - F, "eV")
97+
```
98+
For more information, check https://wiki.fysik.dtu.dk/ase/ase/thermochemistry/thermochemistry.html#id3
99+
100+
## Parallelizing the script
101+
So far, we have a script that performs various calculations on a H/Ni(111) system, namely geometry optimization followed by vibrational frequency calculations, and then computes thermodynamic properties based on those frequencies.
102+
103+
To parallelize this script we are going to use `joblib`. Let’s consider a case where we want to run calculations on different molecules in parallel. Let's assume we have a list of molecules for which you want to calculate the "Total final energy."
104+
#### Dummy Example:
105+
```python
106+
from joblib import Parallel, delayed
107+
108+
def compute(x):
109+
return result
110+
111+
slow_results = [compute(x) for x in data] # Run in series
112+
faster_results = Parallel(n_jobs=4)(delayed(compute(x) for x in data) # Run in parallel with joblib
113+
```
114+
#### Practical Example: CO2, H2O, NH3 - Full Relaxation
115+
```python
116+
from tblite.ase import TBLite
117+
from ase.build import molecule
118+
from ase.optimize import FIRE
119+
from joblib import Parallel, delayed
120+
121+
def calculate_final_energy(molecule_name):
122+
atoms = molecule(molecule_name)
123+
124+
# Set up calculator
125+
calc = TBLite(
126+
method="GFN1-xTB",
127+
accuracy=1.0,
128+
max_iterations=250,
129+
electronic_temperature=300,
130+
)
131+
132+
# Attach to atoms object
133+
atoms.calc = calc
134+
atoms.get_potential_energy()
135+
136+
# Set up optimizer and run
137+
optimizer = FIRE(atoms, trajectory=f'path/to/directory/{molecule_name}_relaxation.traj')
138+
optimizer.run(fmax=0.2)
139+
latest_total_energy = atoms.get_potential_energy()
140+
141+
return molecule_name, latest_total_energy
142+
143+
# Let's assume a list of molecules
144+
molecules = ['CO2', 'H2O', 'NH3']
145+
146+
results = Parallel(n_jobs=-1)(delayed(calculate_final_energy)(molecule_name) for molecule_name in molecules)
147+
148+
for molecule_name, energy in results:
149+
print(f"Total final energy for {molecule_name}:", energy, "eV")
150+
```
151+
1. We've encapsulated the calculations inside a function `calculate_final_energy` which takes a `molecule_name` as an argument.
152+
2. We use `Parallel` and `delayed` from `joblib` to parallelize the calculations across the list of molecules.
153+
3. This script will run the calculations in parallel using all available CPU cores (`n_jobs=-1`). This is not recommended if you try it for the first time.

0 commit comments

Comments
 (0)