11# # Elastic constants
22
3- # We compute *clamped-ion* elastic constants of a crystal using the algorithmic differentiation
4- # density-functional perturbation theory (AD-DFPT) approach as introduced in [^SPH25].
3+ # We compute *clamped-ion* elastic constants of a crystal using
4+ # the algorithmic differentiation density-functional perturbation theory (AD-DFPT) approach
5+ # as introduced in [^SPH25].
56#
67# [^SPH25]:
78# Schmitz, N. F., Ploumhans, B., & Herbst, M. F. (2025)
89# *Algorithmic differentiation for plane-wave DFT: materials design, error control and learning model parameters.*
910# [arXiv:2509.07785](https://arxiv.org/abs/2509.07785)
10-
11+ #
1112# We consider a crystal in its equilibrium configuration, where all atomic forces
1213# and stresses vanish. Homogeneous strains $η$ are then applied
1314# relative to this relaxed structure.
2425# and is tabulated in standard references (eg. Table 9 in [^Nye1985]).
2526# This sparsity can be used a priori to reduce the number of strain patterns
2627# that need to be probed to extract all independent components of $C$.
27- # For example, cubic crystals have only three independent elastic constants $C_{11}$, $C_{12}$ and $C_{44}$,
28- # with the pattern
28+ # For example, cubic crystals have only three independent elastic constants
29+ # $C_{11}$, $C_{12}$ and $C_{44}$, with the pattern
2930# ```math
3031# C = \begin{pmatrix}
3132# C_{11} & C_{12} & C_{12} & 0 & 0 & 0 \\
@@ -84,7 +85,7 @@ function symmetries_from_strain(model0, voigt_strain)
8485 model. symmetries
8586end
8687
87- strain_pattern = [1. , 0. , 0. , 1. , 0. , 0. ]; # should yield [c11, c12, c12, c44, 0, 0] for cubic crystal
88+ strain_pattern = [1. , 0. , 0. , 1. , 0. , 0. ]; # recovers [c11, c12, c12, c44, 0, 0]
8889
8990# For elastic constants beyond the bulk modulus, symmetry-breaking strains
9091# are required. That is, the symmetry group of the crystal is reduced.
@@ -101,22 +102,31 @@ function stress_from_strain(model0, voigt_strain; symmetries, Ecut, kgrid, tol)
101102 DFTK. full_stress_to_voigt (compute_stresses_cart (scfres))
102103end
103104
104- stress_fn (voigt_strain) = stress_from_strain (model0, voigt_strain; symmetries= symmetries_strain, Ecut, kgrid, tol)
105- stress, (dstress,) = value_and_pushforward (stress_fn, AutoForwardDiff (), zeros (6 ), (strain_pattern,));
106- @show stress dstress;
105+ stress_fn (voigt_strain) = stress_from_strain (model0, voigt_strain;
106+ symmetries= symmetries_strain,
107+ Ecut, kgrid, tol)
108+ stress, (dstress,) = value_and_pushforward (stress_fn, AutoForwardDiff (),
109+ zeros (6 ), (strain_pattern,));
110+
111+ # We can inspect the stress to verify it is small (close to equilibrium):
112+ stress
107113
108- c11 = uconvert (u " GPa" , dstress[1 ] * u " hartree" / u " bohr" ^ 3 )
109- c12 = uconvert (u " GPa" , dstress[2 ] * u " hartree" / u " bohr" ^ 3 )
110- c44 = uconvert (u " GPa" , dstress[4 ] * u " hartree" / u " bohr" ^ 3 )
111- @show c11 c12 c44;
114+ # The response of the stress to `strain_pattern` contains the elastic constants
115+ # in atomic units, with the expected pattern $(c11, c12, c12, c44, 0, 0)$:
116+ dstress
112117
113- # These results can be compared directly to finite differences of the stress-strain relation:
118+ # Convert to GPa:
119+ println (" C11: " , uconvert (u " GPa" , dstress[1 ] * u " hartree" / u " bohr" ^ 3 ))
120+ println (" C12: " , uconvert (u " GPa" , dstress[2 ] * u " hartree" / u " bohr" ^ 3 ))
121+ println (" C44: " , uconvert (u " GPa" , dstress[4 ] * u " hartree" / u " bohr" ^ 3 ))
122+
123+ # These results can be compared directly to finite differences of the stress_fn:
114124h = 1e-3
115125dstress_fd = (stress_fn (h * strain_pattern) - stress_fn (- h * strain_pattern)) / 2 h
116- c11_fd = uconvert (u " GPa" , dstress_fd[1 ] * u " hartree" / u " bohr" ^ 3 )
117- c12_fd = uconvert (u " GPa" , dstress_fd[2 ] * u " hartree" / u " bohr" ^ 3 )
118- c44_fd = uconvert (u " GPa" , dstress_fd[4 ] * u " hartree" / u " bohr" ^ 3 )
119- @show c11_fd c12_fd c44_fd;
126+ println ( " C11 (FD): " , uconvert (u " GPa" , dstress_fd[1 ] * u " hartree" / u " bohr" ^ 3 ) )
127+ println ( " C12 (FD): " , uconvert (u " GPa" , dstress_fd[2 ] * u " hartree" / u " bohr" ^ 3 ) )
128+ println ( " C44 (FD): " , uconvert (u " GPa" , dstress_fd[4 ] * u " hartree" / u " bohr" ^ 3 ) )
129+
120130
121131# Here are AD-DFPT results from increasing discretization parameters:
122132#
@@ -127,7 +137,9 @@ c44_fd = uconvert(u"GPa", dstress_fd[4] * u"hartree" / u"bohr"^3)
127137# | 24 | [8, 8, 8] | 153.26 | 56.82 | 99.97 |
128138# | 24 | [14, 14, 14] | 153.03 | 56.71 | 100.09 |
129139#
130- # For comparison, Materials Project for PBE *relaxed-ion* elastic constants of silicon [mp-149](https://next-gen.materialsproject.org/materials/mp-149):
140+ # For comparison, Materials Project for PBE *relaxed-ion* elastic constants of
141+ # silicon [mp-149](https://next-gen.materialsproject.org/materials/mp-149):
131142# c11 = 153 GPa, c12 = 57 GPa, c44 = 74 GPa.
132- # Note the discrepancy in c44, which is due to us not yet including ionic relaxation in this example.
143+ # Note the discrepancy in c44, which is due to us not yet including
144+ # ionic relaxation in this example.
133145
0 commit comments