Find the transformation matrix connecting two POSCAR structures.

Queries about input and output files, running specific calculations, etc.


Moderators: Global Moderator, Moderator

Message
Author
hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Find the transformation matrix connecting two POSCAR structures.

#1 Post by hszhao.cn@gmail.com » Fri Mar 29, 2024 12:28 am

Hi here,

Based on the POSCARs here, I try to find the transformation matrix between the following two POSCARs:

Code: Select all

werner@X10DAi:~$ cat POSCAR_ms
Si2-primitive cell from MS software
1.0
3.8670001030         0.0000000000         0.0000000000
1.9335000515         3.3489203256         0.0000000000
1.9335000515         1.1163067752         3.1573923625
Si
2
Direct
0.000000000         0.000000000         0.000000000
0.250000002         0.250000005         0.249999992

werner@X10DAi:~$ cat POSCAR_vaspkit
Si2-primitive cell from VASPKIT software
  1.000000
0.00000000000000    2.73436403275000    2.73436403275000
2.73436403275000    0.00000000000000    2.73436403275000
2.73436403275000    2.73436403275000    0.00000000000000
Si
2
DIRECT
0.0000000000000000    0.0000000000000000    0.0000000000000000    Si1
0.2500000000000000    0.2500000000000000    0.2500000000000000    Si2
However, this seems to be a very challenging job, I have made some attempts, and I have not been able to reasonably solve this problem. Any tips and comments will be helpful.

See here for the related discussion.

Regards,
Zhao

martin.schlipf
Global Moderator
Global Moderator
Posts: 542
Joined: Fri Nov 08, 2019 7:18 am

Re: Find the transformation matrix connecting two POSCAR structures.

#2 Post by martin.schlipf » Fri Mar 29, 2024 7:41 am

It looks like the first structure is just the second one transformed to standard form. If that is indeed the case, you can use ASE to determine the transformation. Note that this returns the standardized cell and the corresponding transformation.

More generally, this is just a linear problem of two unit cell matrices A and B being related by a unitary transformation U.
A = U B ⇒ U = A B^(-1)

Martin Schlipf
VASP developer


hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#3 Post by hszhao.cn@gmail.com » Fri Mar 29, 2024 11:22 am

Dear martin.schlipf,

Thank you very much for your comments and I will check against your explanations.

Regards,
Zhao

hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#4 Post by hszhao.cn@gmail.com » Tue Apr 02, 2024 6:47 am

Dear martin.schlipf,
More generally, this is just a linear problem of two unit cell matrices A and B being related by a unitary transformation U.
A = U B ⇒ U = A B^(-1)
Here, I am interested in the following questions:

1. What kind of Cell Basis Matrix can ensure that some physical properties obtained from calculations, such as elastic tensors have standard forms, e.g., as listed here in Appendix 3 for the cubic system?
2. If a base transformation is performed, how should the corresponding tensor change?
3. If I want to convert the tensor of a system to the standard form, what kind of base transformation should I do?

martin.schlipf
Global Moderator
Global Moderator
Posts: 542
Joined: Fri Nov 08, 2019 7:18 am

Re: Find the transformation matrix connecting two POSCAR structures.

#5 Post by martin.schlipf » Tue Apr 02, 2024 9:24 am

Vectors within the unit cell transform like the matrix U in this case. For tensors you would need to transform every axis accordingly. Let's do this for a simple example where you use a POSCAR

Code: Select all

nonstandard cell
4.0
0.0 0.0 1.0
0.0 1.0 0.0
1.0 0.0 0.0
...
this will be transformed to standard format

Code: Select all

standard cell
4.0
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
...
A force F = (F_x, F_y, F_z) in the standard cell would correspond to F = (F_z, F_y, F_x) in the nonstandard cell. A stress tensor σ = (σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz) in the standard cell corresponds to σ = (σ_zz, σ_yy, σ_xx, σ_yz, σ_xz, σ_xy) nonstandard cell. More generally, you would apply the U matrix to the vectors/tensors.

You should test this for a simple case where you construct both cells and check that the forces are the same after the transformation. It is very easy to miss a transpose when applying the U matrix from either side so please make sure that you verified this.

Martin Schlipf
VASP developer


hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#6 Post by hszhao.cn@gmail.com » Tue Apr 02, 2024 12:20 pm

A force F = (F_x, F_y, F_z) in the standard cell would correspond to F = (F_z, F_y, F_x) in the nonstandard cell. A stress tensor σ = (σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz) in the standard cell corresponds to σ = (σ_zz, σ_yy, σ_xx, σ_yz, σ_xz, σ_xy) nonstandard cell. More generally, you would apply the U matrix to the vectors/tensors.
By saying the standard cell, do you mean the one obtained by standard_form command of ase? But as you can see, this method uses different thransformation matrices from spglib. So, I still cannot undertand what do you mean by saying the standard cell, or even worse, I don't think there is a absolute standard for defining the standard cell.

Any more tips and further explanations will be helpful.

Regards,
Zhao

martin.schlipf
Global Moderator
Global Moderator
Posts: 542
Joined: Fri Nov 08, 2019 7:18 am

Re: Find the transformation matrix connecting two POSCAR structures.

#7 Post by martin.schlipf » Tue Apr 02, 2024 2:05 pm

A standard cell can be mapped directly onto three lengths of the unit cell vectors (a, b, c) and angles between them (alpha, beta, gamma). Naturally, there is still the degree of freedom of permutation. The x axis is then chosen parallel to the first vector and the y axis in the plane spanned by the first two vectors. For calculations like in VASP, this default is often inconvenient because you want to align the x axis along a specific direction e.g. in an fcc cell you may want the coordinate axis to align with the cubic conventional cell and not with the vectors of the primitive cell. Hence, VASP gives you the option to specify the vectors explicitly. Still, all choices for the same cell should be related by a unitary transformation and by applying the transformation or its inverse you can go from one coordinate system to the other.

Martin Schlipf
VASP developer


hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#8 Post by hszhao.cn@gmail.com » Tue Apr 02, 2024 2:44 pm

Dear martin.schlipf,

Thank you for your wonderful comments on the standard cell used by VASP by default. I want to add the following additional remarks as a supplementary:

1. In fact, a unitary transformation should only be an orthogonal transformation in this case, considering that we're working in the real number field.

2. For the standards of defining conventional cells, there are some different standards. Below are the most widely used ones, as described here:

(a) The standards defined in Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools, and Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010.
They basically enforce as much as possible norm(a1)<norm(a2)<norm(a3).
(b) The standards defined in the International Tables of Crystallography.

hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#9 Post by hszhao.cn@gmail.com » Wed Apr 03, 2024 1:45 am

Some further additional remarks:

1. If we only consider the relationship between the two sets of cell basis vectors, yes, a change-of-basis transformation is enough. But, if we also consider identifying the exact match/transformation relations between the positions of the ions in two structures, an origin-shift may also be necessary, as shown below:

Code: Select all

from pymatgen.core import Structure
from pymatgen.analysis.structure_matcher import StructureMatcher
 
poscar1 = 'POSCAR_ms'
poscar2 = 'POSCAR_vesta'
 
structure1 = Structure.from_file(poscar1)
structure2 = Structure.from_file(poscar2)
 
matcher = StructureMatcher(primitive_cell=False)
matches = matcher.fit(structure1, structure2)
 
if matches:
    try:
        transformation = matcher.get_transformation(structure1, structure2)
        print(transformation)
    except ValueError as e:
        print(f"error:{str(e)}")
else:
    print("Failed to match.")

(array([[ 0,  0, -1],
       [ 0, -1,  0],
       [-1,  0,  0]]), array([0.12499998, 0.12500001, 0.12499999]), [0, 1])
2. If we also consider the space group representation corresponding to these two structures, Bilbao Crystallographic Server IDENTIFY GROUP shows that they are exactly the same, in this case:

Code: Select all

POSCAR_ms
Transformation Matrix to the standard/default setting
(	

    1   -1   -1     1/8
    1    1    1    -3/8
   -1   -1    1     1/8

	)

POSCAR_vaspkit	
Transformation Matrix to the standard/default setting
(	

    1   -1   -1     1/8
    1    1    1    -3/8
   -1   -1    1     1/8

	)
3. I also further checked the following two POSCARs listed on the webpage via Bilbao Crystallographic Server IDENTIFY GROUP, the results are as follows:

Code: Select all

$ cat POSCAR_aflow 
Si2-primitive cell from AFLOW software
1.224745
   0.00000000000000   2.23259888375212   2.23259888375212
   2.23259888375212   0.00000000000000   2.23259888375212
   2.23259888375212   2.23259888375212   0.00000000000000
2 
Direct(2) [A2] 
   0.00000000000000   0.00000000000000   0.00000000000000 
   0.75000000000000   0.75000008800000   0.75000000000000 
The transformation matrix to the standard/default setting of BCS:

Code: Select all

POSCAR_aflow	
Transformation Matrix to the standard/default setting
(	

    1   -1   -1    -1/8
    1    1    1     3/8
   -1   -1    1    -1/8

	)

Code: Select all

$ cat POSCAR_vesta 
Si2-primitive cell from VESTA software
1.0
3.8670001030         0.0000000000         0.0000000000
1.9335000515         3.3489203256         0.0000000000
1.9335000515         1.1163067752         3.1573923625
Si
2
Direct
0.125000001         0.125000002         0.124999996
0.874999978         0.875000019         0.874999963
The transformation matrix to the standard/default setting of BCS:

Code: Select all

POSCAR_vesta
Transformation Matrix to the standard/default setting
(	

   1  -1  -1      0
   1   1   1    1/2
  -1  -1   1      0

	)
See here for the related discussion.

Regards,
Zhao

hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#10 Post by hszhao.cn@gmail.com » Wed Apr 03, 2024 2:23 am

2. If we also consider the space group representation corresponding to these two structures, Bilbao Crystallographic Server IDENTIFY GROUP shows that they are the same, in this case:
Additional remarks:
1. Below is the Python code snippet using pymatgen to obtain the space group representation corresponding to a given POSCAR:

Code: Select all

from pymatgen.core import Structure
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
import numpy as np
from fractions import Fraction

def to_fraction_matrix(matrix):
    # Function to convert a matrix into its fractional form
    fraction_matrix = np.vectorize(lambda x: str(Fraction(x).limit_denominator()))(matrix)
    return fraction_matrix

# Create a Structure instance using the provided POSCAR data
structure = Structure.from_file("./POSCAR_vaspkit")

# Analyze the structure using SpacegroupAnalyzer
analyzer = SpacegroupAnalyzer(structure)

# Get the symmetry operations (rotation and translation operations) of the space group
symmetry_operations = analyzer.get_symmetry_operations()

affine_matrices_in_fractions = []  # List to store all affine matrices in fractional form

for op in symmetry_operations:
    rot = op.rotation_matrix  # Rotation matrix
    trans = op.translation_vector  # Translation vector

    # Create a 4x4 identity matrix to construct the affine matrix
    affine_matrix = np.eye(4)
    affine_matrix[:3, :3] = rot  # Fill the top-left 3x3 part with the rotation matrix
    affine_matrix[:3, 3] = trans  # Fill the top three rows of the rightmost column with the translation vector

    # Convert the affine matrix to fractional form and add it to the list
    affine_matrices_in_fractions.append(to_fraction_matrix(affine_matrix))

# Convert each affine matrix in the list to the desired format
formatted_affine_matrices = []
for matrix in affine_matrices_in_fractions:
    formatted_matrix = [list(row) for row in matrix]
    formatted_affine_matrices.append(formatted_matrix)

# Print or return the list of all affine matrices in fractional form
print(formatted_affine_matrices)
The above script will give the generators of the corresponding space group, in this case, they are the coset representatives with respect to the (maximum) translation group shown below:

Code: Select all

POSCAR_ms:=[[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]]

POSCAR_vaspkit:=[[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]]
Then the above generators can be tested with my CrystKit package as follows:

Code: Select all

POSCAR_ms:=[[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]];;

POSCAR_vaspkit:=[[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]];;

S1:=AffineCrystGroupOnLeft(POSCAR_ms);
S2:=AffineCrystGroupOnLeft(POSCAR_vaspkit);
AffineIsomorphismSpaceGroups(S1, S2);
This will give the following result:

Code: Select all

gap> AffineIsomorphismSpaceGroups(S1, S2);
[ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ] ]
2. Use the following Python code snippet to obtain the generators for comparison based on Bilbao Crystallographic Server IDENTIFY GROUP:

Code: Select all

from pymatgen.core import Structure
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

structure = Structure.from_file("./POSCAR_vaspkit")
analyzer = SpacegroupAnalyzer(structure)

space_group_symbol = analyzer.get_space_group_symbol()
print(f"Space Group Symbol: {space_group_symbol}")

symmetry_operations = analyzer.get_symmetry_operations()
for i, op in enumerate(symmetry_operations, start=1):
    #print(f"Operation {i}:")
    print(op.as_xyz_str())

martin.schlipf
Global Moderator
Global Moderator
Posts: 542
Joined: Fri Nov 08, 2019 7:18 am

Re: Find the transformation matrix connecting two POSCAR structures.

#11 Post by martin.schlipf » Wed Apr 03, 2024 6:29 am

Good point: in general, a translation is also allowed. So for all quantities that corresponds to a position in space instead of differences between two positions, you need to account for this translation as well.

Martin Schlipf
VASP developer


hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#12 Post by hszhao.cn@gmail.com » Wed Apr 03, 2024 1:45 pm

It also should be noted that there are infinite conjugators connecting two isomorphism space groups. From further verification, it can be shown that both the results given by BCS identify group and pymatgen StructureMatcher are correct:

Code: Select all

In [2]: from pymatgen.core import Structure
   ...: from pymatgen.analysis.structure_matcher import StructureMatcher
   ...: 
   ...: poscar1 = 'POSCAR_ms'
   ...: poscar2 = 'POSCAR_vaspkit'
   ...: 
   ...: structure1 = Structure.from_file(poscar1)
   ...: structure2 = Structure.from_file(poscar2)
   ...: 
   ...: matcher = StructureMatcher(primitive_cell=False)
   ...: matches = matcher.fit(structure1, structure2)
   ...: 
   ...: if matches:
   ...:     try:
   ...:         transformation = matcher.get_transformation(structure1, structur
   ...: e2)
   ...:         print(transformation)
   ...:     except ValueError as e:
   ...:         print(f"error:{str(e)}")
   ...: else:
   ...:     print("Failed to match.")
   ...: 
(array([[ 0,  0, -1],
       [ 0, -1,  0],
       [-1,  0,  0]]), array([0.25, 0.25, 0.25]), [1, 0])

In [4]: from pymatgen.core import Structure
   ...: from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
   ...: import numpy as np
   ...: from fractions import Fraction
   ...: 
   ...: def to_fraction_matrix(matrix):
   ...:     # Function to convert a matrix into its fractional form
   ...:     fraction_matrix = np.vectorize(lambda x: str(Fraction(x).limit_denominator()))(matrix)
   ...:     return fraction_matrix
   ...: 
   ...: # Create a Structure instance using the provided POSCAR data
   ...: structure = Structure.from_file("./POSCAR_ms")
   ...: 
   ...: # Analyze the structure using SpacegroupAnalyzer
   ...: analyzer = SpacegroupAnalyzer(structure)
   ...: 
   ...: # Get the symmetry operations (rotation and translation operations) of the space group
   ...: symmetry_operations = analyzer.get_symmetry_operations()
   ...: 
   ...: affine_matrices_in_fractions = []  # List to store all affine matrices in fractional form
   ...: 
   ...: for op in symmetry_operations:
   ...:     rot = op.rotation_matrix  # Rotation matrix
   ...:     trans = op.translation_vector  # Translation vector
   ...: 
   ...:     # Create a 4x4 identity matrix to construct the affine matrix
   ...:     affine_matrix = np.eye(4)
   ...:     affine_matrix[:3, :3] = rot  # Fill the top-left 3x3 part with the rotation matrix
   ...:     affine_matrix[:3, 3] = trans  # Fill the top three rows of the rightmost column with the translation vector
   ...: 
   ...:     # Convert the affine matrix to fractional form and add it to the list
   ...:     affine_matrices_in_fractions.append(to_fraction_matrix(affine_matrix))
   ...: 
   ...: # Convert each affine matrix in the list to the desired format
   ...: formatted_affine_matrices = []
   ...: for matrix in affine_matrices_in_fractions:
   ...:     formatted_matrix = [list(row) for row in matrix]
   ...:     formatted_affine_matrices.append(formatted_matrix)
   ...: 
   ...: # Print or return the list of all affine matrices in fractional form
   ...: print(formatted_affine_matrices)

In [3]: from pymatgen.core import Structure
   ...: from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
   ...: import numpy as np
   ...: from fractions import Fraction
   ...: 
   ...: def to_fraction_matrix(matrix):
   ...:     # Function to convert a matrix into its fractional form
   ...:     fraction_matrix = np.vectorize(lambda x: str(Fraction(x).limit_denominator()))(matrix)
   ...:     return fraction_matrix
   ...: 
   ...: # Create a Structure instance using the provided POSCAR data
   ...: structure = Structure.from_file("./POSCAR_vaspkit")
   ...: 
   ...: # Analyze the structure using SpacegroupAnalyzer
   ...: analyzer = SpacegroupAnalyzer(structure)
   ...: 
   ...: # Get the symmetry operations (rotation and translation operations) of the space group
   ...: symmetry_operations = analyzer.get_symmetry_operations()
   ...: 
   ...: affine_matrices_in_fractions = []  # List to store all affine matrices in fractional form
   ...: 
   ...: for op in symmetry_operations:
   ...:     rot = op.rotation_matrix  # Rotation matrix
   ...:     trans = op.translation_vector  # Translation vector
   ...: 
   ...:     # Create a 4x4 identity matrix to construct the affine matrix
   ...:     affine_matrix = np.eye(4)
   ...:     affine_matrix[:3, :3] = rot  # Fill the top-left 3x3 part with the rotation matrix
   ...:     affine_matrix[:3, 3] = trans  # Fill the top three rows of the rightmost column with the translation vector
   ...: 
   ...:     # Convert the affine matrix to fractional form and add it to the list
   ...:     affine_matrices_in_fractions.append(to_fraction_matrix(affine_matrix))
   ...: 
   ...: # Convert each affine matrix in the list to the desired format
   ...: formatted_affine_matrices = []
   ...: for matrix in affine_matrices_in_fractions:
   ...:     formatted_matrix = [list(row) for row in matrix]
   ...:     formatted_affine_matrices.append(formatted_matrix)
   ...: 
   ...: # Print or return the list of all affine matrices in fractional form
   ...: print(formatted_affine_matrices)
Then check them in gap as follows:

Code: Select all

gap> POSCAR_ms:=[[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]];

gap> POSCAR_vaspkit:=[[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 1, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [0, -1, 0, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [0, 0, 1, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [0, 0, -1, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[0, -1, 0, 1/4], [0, 0, -1, 1/4], [-1, 0, 0, 1/4], [0, 0, 0, 1]], [[0, 0, 1, 0], [1, 0, 0, 0], [-1, -1, -1, 0], [0, 0, 0, 1]], [[-1, 0, 0, 1/4], [1, 1, 1, 1/4], [0, -1, 0, 1/4], [0, 0, 0, 1]], [[-1, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], [[0, 0, -1, 1/4], [0, -1, 0, 1/4], [1, 1, 1, 1/4], [0, 0, 0, 1]], [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], [[1, 1, 1, 1/4], [-1, 0, 0, 1/4], [0, 0, -1, 1/4], [0, 0, 0, 1]], [[0, 1, 0, 0], [-1, -1, -1, 0], [1, 0, 0, 0], [0, 0, 0, 1]]];

gap> S1:=AffineCrystGroupOnLeft(POSCAR_ms);
<matrix group with 48 generators>
gap> S2:=AffineCrystGroupOnLeft(POSCAR_vaspkit);
<matrix group with 48 generators>
gap> # This is the result given by pymatgen StructureMatcher:
gap> conj:=AugmentedMatrixOnLeft([[ 0,  0, -1],
>        [ 0, -1,  0],
>        [-1,  0,  0]], [1/4, 1/4, 1/4]);
[ [ 0, 0, -1, 1/4 ], [ 0, -1, 0, 1/4 ], [ -1, 0, 0, 1/4 ], [ 0, 0, 0, 1 ] ]
gap> S1^(conj^-1)=S1;
true
gap> # This is the result given by BCS identify group:
gap> S1=S2;
true
See here for the related discussion.

hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#13 Post by hszhao.cn@gmail.com » Wed Apr 03, 2024 1:49 pm

I have attached all the POSCARs used here.
You do not have the required permissions to view the files attached to this post.

hszhao.cn@gmail.com
Full Member
Full Member
Posts: 191
Joined: Tue Oct 13, 2020 11:32 pm

Re: Find the transformation matrix connecting two POSCAR structures.

#14 Post by hszhao.cn@gmail.com » Wed Apr 03, 2024 2:44 pm

But I still have some puzzles when trying to understand the change-of-basis matrix which connects two POSCARs, see below for a more detailed description.

The two POSCARs are listed below:

Code: Select all

$ cat POSCAR_ms 
Si2-primitive cell from MS software
1.0
3.8670001030         0.0000000000         0.0000000000
1.9335000515         3.3489203256         0.0000000000
1.9335000515         1.1163067752         3.1573923625
Si
2
Direct
0.000000000         0.000000000         0.000000000
0.250000002         0.250000005         0.249999992

$ cat POSCAR_vaspkit 
Si2-primitive cell from VASPKIT software
  1.000000
0.00000000000000    2.73436403275000    2.73436403275000
2.73436403275000    0.00000000000000    2.73436403275000
2.73436403275000    2.73436403275000    0.00000000000000
Si
2
DIRECT
0.0000000000000000    0.0000000000000000    0.0000000000000000    Si1
0.2500000000000000    0.2500000000000000    0.2500000000000000    Si2
Then I try to find the affine transformation matrix between them which maps poscar1 to poscar2:

Code: Select all

In [13]: from pymatgen.core import Structure
    ...: from pymatgen.analysis.structure_matcher import StructureMatcher
    ...: 
    ...: poscar1 = 'POSCAR_ms'
    ...: poscar2 = 'POSCAR_vaspkit'
    ...: 
    ...: structure1 = Structure.from_file(poscar1)
    ...: structure2 = Structure.from_file(poscar2)
    ...: 
    ...: matcher = StructureMatcher(primitive_cell=False)
    ...: matches = matcher.fit(structure1, structure2)
    ...: 
    ...: if matches:
    ...:     try:
    ...:         transformation = matcher.get_transformation(structure1, structure2)
    ...:         print(transformation)
    ...:     except ValueError as e:
    ...:         print(f"error:{str(e)}")
    ...: else:
    ...:     print("Failed to match.")
    ...: 
(array([[ 0,  0, -1],
       [ 0, -1,  0],
       [-1,  0,  0]]), array([0.25, 0.25, 0.25]), [1, 0])
As we can see the change-of-basis matrix obtained above is:

Code: Select all

       [[ 0,  0, -1],
       [ 0, -1,  0],
       [-1,  0,  0]]
But this matrix doesn't transform the cell basis of poscar1 to poscar2:

Code: Select all

In [18]: import numpy as np
    ...: 
    ...: # Define the basis vectors of the first Si2-primitive cell (MS software)
    ...: A1 = np.array([
    ...:     [3.8670001030, 0.0000000000, 0.0000000000],
    ...:     [1.9335000515, 3.3489203256, 0.0000000000],
    ...:     [1.9335000515, 1.1163067752, 3.1573923625]
    ...: ])
    ...: 
    ...: # Define the transformation matrix
    ...: T = np.array([
    ...:     [0, 0, -1],
    ...:     [0, -1, 0],
    ...:     [-1, 0, 0]
    ...: ])
    ...: 
    ...: # Apply the transformation matrix to A1
    ...: transformed_A1 = np.dot(A1.T, T).T
    ...: 
    ...: print("Transformed A1:\n", transformed_A1)
    ...: 
    ...: # Define the basis vectors of the second Si2-primitive cell (VASPKIT software) for comparison
    ...: A2 = np.array([
    ...:     [0.00000000000000, 2.73436403275000, 2.73436403275000],
    ...:     [2.73436403275000, 0.00000000000000, 2.73436403275000],
    ...:     [2.73436403275000, 2.73436403275000, 0.00000000000000]
    ...: ])
    ...: 
    ...: # Print A2 for visual comparison
    ...: print("A2:\n", A2)
Transformed A1:
 [[-1.93350005 -1.11630678 -3.15739236]
 [-1.93350005 -3.34892033  0.        ]
 [-3.8670001   0.          0.        ]]
A2:
 [[0.         2.73436403 2.73436403]
 [2.73436403 0.         2.73436403]
 [2.73436403 2.73436403 0.        ]]
Where exactly did my understanding go wrong?

Regards,
Zhao

martin.schlipf
Global Moderator
Global Moderator
Posts: 542
Joined: Fri Nov 08, 2019 7:18 am

Re: Find the transformation matrix connecting two POSCAR structures.

#15 Post by martin.schlipf » Thu Apr 04, 2024 6:44 am

At this point, I would advise to ask this on the pymatgen discussion page. I cannot comment on what these routines are supposed to do. It is clear that the resulting transformation is not the one that lets you go from one POSCAR to the other one. With ASE this works

Code: Select all

import ase
import numpy as np
x = 2.73436403275000
old_cell = ase.cell.Cell([[0, x, x], [x, 0, x], [x, x, 0]])
new_cell, U = old_cell.standard_form()
assert np.allclose(new_cell @ U, old_cell)
print(new_cell)
# Output: Cell([[3.86697469958024, 0.0, -0.0], [1.9334873497901195, 3.348898325628185, -0.0], [1.9334873497901195, 1.1162994418760608, 3.157371620741286]])

Martin Schlipf
VASP developer


Locked