# Introduction
Given two structures, this code snippet is used to compute the transformation matrix from a "source" structure to a "destination" structure. The most common example is the source being the primitive cell and destination the conventional cell, but this can be used for pretty much anything.
Essentially, all we're doing is finding a matrix $T$ such that
$
\begin{aligned}
\left(\boldsymbol{a}_d, \boldsymbol{b}_d, \boldsymbol{c}_d\right)= & (\boldsymbol{a}_s, \boldsymbol{b}_s, \boldsymbol{c}_s) \boldsymbol{T} \\
\end{aligned}
$
where $a, b, c$ are the lattice constants and the $s$ subscript denotes source and $d$ denotes destination. We're using VESTA's convention here.
We can define our lattice matrices as
$
\begin{align}
\boldsymbol{D} &= \left(\boldsymbol{a}_d, \boldsymbol{b}_d, \boldsymbol{c}_d\right),\\
\boldsymbol{S} &= \left(\boldsymbol{a}_s, \boldsymbol{b}_s, \boldsymbol{c}_s\right)
\end{align}
$
so we simply have
$
\boldsymbol{T} = \boldsymbol{S}^{-1} \boldsymbol{D}
$
>[!WARNING]
> `pymatgen` uses the opposite convention to VESTA, i.e., the transformation matrix used by, e.g., `Structure.make_supercell` and `SymmOp.from_rotation_and_translation`, is the transpose of the one shown above.
^921dcc
# Option 1: Pymatgen
This is the most straightforward way to do it. If you already have your structures in `POSCAR` or `.cif` formats (or anything else `pymatgen` supports), it's easy to do it this way.
```python
import numpy as np
from pymatgen.core.structure import Structure
# Get structures from files
source = Structure.from_file("source.vasp") # e.g., primitive
destination = Structure.from_file("destination.vasp") # e.g., conventional
# Define the lattice matrices
S = source.lattice.matrix.T
D = destination.lattice.matrix.T
# Compute transformation matrix
T = np.matmul(np.linalg.inv(S), D)
```
# Option 2: By hand
You can just define your lattice matrices by hand if you choose to. For example, if you have your structures in Quantum ESPRESSO formats, `pymatgen` can't read them (but you can and should just convert them to `cif` using, e.g., [cif2cell](https://github.com/torbjornbjorkman/cif2cell)). Either way, if your QE input files look like this
##### Source:
```
CELL_PARAMETERS angstrom
4.2972855568 0.0000000000 0.0000000000
0.0000000000 4.2972855568 0.0000000000
2.1486428272 2.1486428272 4.8912462131
```
##### Destination:
```
CELL_PARAMETERS angstrom
-2.1486427784 2.1486427784 4.8912463188
2.1486427784 -2.1486427784 4.8912463188
2.1486427784 2.1486427784 -4.8912463188
```
Just define your lattice matrices manually:
```python
a_d = [-2.1486427784, 2.1486427784, 4.8912463188]
b_d = [2.1486427784, -2.1486427784, 4.8912463188]
c_d = [2.1486427784, 2.1486427784, -4.8912463188]
a_s = [4.2972855568, 0.0000000000, 0.0000000000]
b_s = [0.0000000000, 4.2972855568, 0.0000000000]
c_s = [2.1486428272, 2.1486428272, 4.8912462131]
D = np.vstack((a_d, b_d, c_d)).T
S = np.vstack((a_s, b_s, c_s)).T
```
And proceed as before to compute `T`.
# Tips
1. See the [[Quick Transformation Matrix#^921dcc|warning]] above regarding conventions.
2. `VESTA` generally only accepts integral and half-integral transformation matrices. It won't throw an error but you'll end up with a mess.
3. `pymatgen`'s `Structure.make_supercell` method only accepts integral transformation matrices.
4. You can generate use an arbitrary rotation matrix in `pymatgen` as follows
```python
from pymatgen.core.structure import Structure
from pymatgen.core.operations import SymmOp
# load file
my_struct = Structure.from_file("struct.cif")
# Don't use random numbers...
# Shift Origin (if necessary)
s = np.random.rand(1,3)
# Transfromation matrix
# See note above about VESTA vs pmg convention
T = np.random.rand(3,3)
shift = np.rand((3,1))
symop = SymmOp.from_rotation_and_translation(rotation_matrix=T, translation_vec=s)
my_struct.apply_operation(symop, fractional=True)
```
5. As a sanity check, if our transformation matrix is given by
$
\begin{aligned}
\boldsymbol{T} = \left(\begin{array}{lll}
T_{11} & T_{12} & T_{13} \\
T_{21} & T_{22} & T_{23} \\
T_{31} & T_{32} & T_{33}
\end{array}\right) \\
\end{aligned}
$
Then we can define a rescaled transformation matrix
$
\begin{aligned}
\boldsymbol{T}^{\prime} = \left(\begin{array}{lll}
T^{\prime}_{11} & T^{\prime}_{12} & T^{\prime}_{13} \\
T^{\prime}_{21} & T^{\prime}_{22} & T^{\prime}_{23} \\
T^{\prime}_{31} & T^{\prime}_{32} & T^{\prime}_{33}
\end{array}\right) \\
\end{aligned}
$
where
$
T_{ij}^{\prime} = \frac{T_{ij}}{\sqrt{\sum_{k=1}^{3} T_{ik}^2}}, \quad \forall j \in \{1,2,3\}
$
(Be careful with conventions here, again, see the [[Quick Transformation Matrix#^921dcc|warning]].)
Then, $\pmb{T}^\prime$ should be an $O(3)$ matrix, i.e.,
$
\begin{align}
|\det T| &= 1, \\
T^{T} = T^{-1} \implies T^TT = TT^T &= \mathbb{1}
\end{align}
$
We can check this in python easily
```python
import numpy as np
# Transforms c axis of simple cubic to point in[-2 1 1] direction
T = np.array([[1, 1, 1], [0, -1, 1], [2, -1, -1]])
np.linalg.det(T) # 6
# Computes scale
s = np.array([np.sqrt(np.sum([T[i,j]**2 for j in range(3)])) for i in range(3)])
T_prime = T/s[:,None]
np.any(np.isclose(np.linalg.inv(T_prime), T_prime.T)) # True
np.abs(np.linalg.det(T_prime)) # 1.0
```