Density functional theory (DFT) has proven to be an effective way to obtain the physical properties of material systems. Using the linear stress-strain relationship, one is able to obtain the elastic constants from a set of DFT calculations. One particular challenge is that the elastic tensor thus obtained is usually not numerically symmetric with respect to the crystal symmetry. Neumann's principle, or principle of symmetry, states that 1:
If a crystal is invariant with respect to certain symmetry operations, any of its physical properties must also be invariant with respect to the same symmetry operations, or otherwise stated, the symmetry operations of any physical property of a crystal must include the symmetry operations of the point group of the crystal.
Therefore, it is worthwhile working out a routine to symmetrize the elastic tensor using the symmetry operations of the crystal.
A
Suppose the space group of the structure is
So the idea is let all the rotations in
Luckily, our effort doesn't have to go from scratch. There are excellent packages in Python to deal with basic math and linear algebra, such as NumPy4, and to deal with crystal structures, pymatgen5, leveraging the spglib6 to handle symmetry detection and obtain space group operations.
Here we assume users have installed Python 3.x and packages NumPy and pymatgen (v.3.4.0). We define a new function that takes in a
def get_symmetrized_elastic_tensor(C, structure, align=False, tol=1e-4):
"""
Parameters:
C: a 6 x 6 elasic tensor.
structure: the structure used in the calculation, here given to detect symmetry.
align: if the tensor is provided in the standard orientation, but the structure is not,
switch to True to align the structure.
tol: tolerance for pymatgen.analysis.elasticity.elastic.ElasticTensor initializer,
here default to 1e-4 rather than 1e-5.
Returns:
C_prime: the symmetrized elastic tensor.
"""
import numpy as np
from pymatgen.analysis.elasticity.elastic import ElasticTensor
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
# get the space group analyzer of the primitive cell.
sga = SpacegroupAnalyzer(SpacegroupAnalyzer(structure).find_primitive())
if align:
sga = SpacegroupAnalyzer(sga.get_primitive_standard_structure())
# get the rotational parts of symmetry operations of the space group in the cartesian coordinates.
ops_list = sga.get_symmetry_operations(cartesian=True)
ops_rotation_list = [op.rotation_matrix for op in ops_list]
# symmetrize C with $C_{ij} = C_{ji}$.
C = ElasticTensor(ElasticTensor(C).symmetrized)
# average the rotated tensors.
C_prime_sum = 0
for idx, op in enumerate(ops_rotation_list):
# perform the 4th order tensor rotation.
C_prime = ElasticTensor.from_full_tensor(np.einsum('im,jn,mnop,ok,pl', op, op, C.full_tensor, op.T, op.T), tol=tol)
C_prime_sum += C_prime
C_prime = C_prime_sum / (idx + 1)
# print out some useful information.
print(sga.get_spacegroup_symbol(), sga.get_point_group(), len(ops_rotation_list))
return C_prime
Here is a test, getting the structures and elastic tensors from the Materials Project7:
import pymatgen as mg
import pandas as pd
def get_MP_data(chemsys_formula_id, data_type='vasp', prop=''):
m = mg.MPRester()
df = pd.io.json.json_normalize(m.get_data(chemsys_formula_id, data_type, prop)).set_index('material_id')\
.drop(['spacegroup.source', 'spacegroup.point_group', 'spacegroup.hall'], axis=1)
for col in df.columns:
if 'unit_cell_formula.' in col:
df.drop(col, axis=1, inplace=True)
return df
# test
for i in ['Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn']:
print(i)
for idx, data in get_MP_data(i + '-N').iterrows():
if ('elasticity.elastic_tensor' in data) and (not np.isnan(data['elasticity.elastic_tensor']).all()):
st = mg.Structure.from_str(data['cif'], fmt='cif')
C = pd.DataFrame(data['elasticity.elastic_tensor'], index=range(1, 7), columns=range(1, 7))
C_prime = pd.DataFrame(get_symmetrized_elastic_tensor(C, st, align=True), index=range(1, 7), columns=range(1, 7))
print(C_prime)
print(C_prime - C)
print('=====')
Footnotes
-
J.F. Nye, Physical Properties of Crystals: Their Representation by Tensors and Matrices (Clarendon Press, 1985). ↩
I cant finish the test
it would reply this:
Sc Traceback (most recent call last): File "yy.py", line 13, in <module> for idx, data in get_MP_data(i + '-N').iterrows(): File "yy.py", line 2, in get_MP_data m = mg.MPRester() NameError: global name 'mg' is not defined
what should i do please?