-
Notifications
You must be signed in to change notification settings - Fork 62
/
polar.py
106 lines (82 loc) · 3.37 KB
/
polar.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
""" Core definition for Polar property Document """
from datetime import datetime
from typing import ClassVar, Dict, List, Tuple
import numpy as np
from pydantic import BaseModel, Field
from emmet.core import SETTINGS
from emmet.core.material import PropertyDoc
from emmet.core.structure import StructureMetadata
from emmet.stubs import Matrix3D, Vector3D
from pymatgen.analysis.piezo import PiezoTensor as BasePiezoTensor
VoigtVector = Tuple[float, float, float, float, float, float]
PiezoTensor = Tuple[VoigtVector, VoigtVector, VoigtVector]
PiezoTensor.__doc__ = "Rank 3 real space tensor in Voigt notation" # type: ignore
class Dielectric(PropertyDoc):
"""
A dielectric property block
"""
property_name: ClassVar[str] = "dielectric"
total: Matrix3D = Field(description="Total dielectric response")
ionic: Matrix3D = Field(
description="Dielectric response due to atomic rearrangement"
)
electronic: Matrix3D = Field(
description="Dielectric response due to electron rearrangement"
)
e_total: float = Field(description="Total electric permittivity")
e_ionic: float = Field(
description="Electric permittivity from atomic rearrangement"
)
e_electronic: float = Field(
description="Electric permittivity due to electrons rearrangement"
)
n: float = Field(title="Refractive index")
@classmethod
def from_ionic_and_electronic(cls, ionic: Matrix3D, electronic: Matrix3D):
total = np.sum(ionic, electronic).tolist()
return cls(
**{
"total": total,
"ionic": ionic,
"electronic": electronic,
"e_total": np.average(np.diagonal(total)),
"e_ionic": np.average(np.diagonal(ionic)),
"e_electronic": np.average(np.diagonal(electronic)),
"n": np.sqrt(np.average(np.diagonal(electronic))),
}
)
class Piezoelectric(PropertyDoc):
"""
A dielectric package block
"""
property_name: ClassVar[str] = "piezoelectric"
total: PiezoTensor = Field(None, description="")
ionic: PiezoTensor = Field(None, description="")
electronic: PiezoTensor = Field(None, description="")
e_ij_max: float = Field(None, description="")
max_direction: Tuple[int, int, int] = Field(
None, description="Miller direction for maximum piezo response"
)
strain_for_max: Matrix3D = Field(
None, description="Normalized strain direction for maximum piezo repsonse"
)
@classmethod
def from_ionic_and_electronic(cls, ionic: Matrix3D, electronic: Matrix3D):
total = BasePiezoTensor.from_voigt(np.sum(ionic, electronic))
directions, charges, strains = np.linalg.svd(total, full_matrices=False)
max_index = np.argmax(np.abs(charges))
max_direction = directions[max_index]
# Allow a max miller index of 10
min_val = np.abs(max_direction)
min_val = min_val[min_val > (np.max(min_val) / SETTINGS.MAX_PIEZO_MILLER)]
min_val = np.min(min_val)
return cls(
**{
"total": total.zeroed().voigt,
"ionic": ionic,
"static": electronic,
"e_ij_max": charges[max_index],
"max_direction": np.round(max_direction / min_val),
"strain_for_max": strains[max_index],
}
)