SIRIUS 7.5.0
Electronic structure library and applications
lattice_relaxation.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2022 Anton Kozhevnikov, Thomas Schulthess
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
5// the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
8// following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10// and the following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
13// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
15// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
17// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20/** \file lattice_relaxation.hpp
21 *
22 * \brief Lattice relaxation implementation.
23 */
24
25#ifndef __LATTICE_RELAXATION_HPP__
26#define __LATTICE_RELAXATION_HPP__
27
28#include "dft_ground_state.hpp"
29#if defined(SIRIUS_VCSQNM)
31#endif
32
33namespace sirius {
34
36{
37 private:
38 DFT_ground_state& dft_;
39 public:
41 : dft_{dft__}
42 {
43 }
44
45 nlohmann::json find(int max_num_steps__, double forces_thr__ = 1e-4, double stress_thr__ = -1e-4)
46 {
47 nlohmann::json result;
48#if defined(SIRIUS_VCSQNM)
49 bool compute_stress = stress_thr__ > 0;
50 bool compute_forces = forces_thr__ > 0;
51
52 int na = dft_.ctx().unit_cell().num_atoms();
53
54 std::unique_ptr<vcsqnm::PES_optimizer::periodic_optimizer> geom_opt;
55 Eigen::MatrixXd r(3, na);
56 Eigen::MatrixXd f(3, na);
57 Eigen::Vector3d lat_a, lat_b, lat_c;
58 for (int x = 0; x < 3; x++) {
59 lat_a[x] = dft_.ctx().unit_cell().lattice_vector(0)[x];
60 lat_b[x] = dft_.ctx().unit_cell().lattice_vector(1)[x];
61 lat_c[x] = dft_.ctx().unit_cell().lattice_vector(2)[x];
62 }
63
64 Eigen::Matrix3d stress;
65
66 for (int ia = 0; ia < na; ia++) {
67 for (auto x: {0, 1, 2}) {
68 r(x, ia) = dft_.ctx().unit_cell().atom(ia).position()[x];
69 }
70 }
71 /*
72 * @param initial_step_size initial step size. default is 1.0. For systems with hard bonds (e.g. C-C) use a value between and 1.0 and
73 * 2.5. If a system only contains weaker bonds a value up to 5.0 may speed up the convergence.
74 * @param nhist_max Maximal number of steps that will be stored in the history list. Use a value between 3 and 20. Must be <= than 3*nat + 9.
75 * @param lattice_weight weight / size of the supercell that is used to transform lattice derivatives. Use a value between 1 and 2. Default is 2.
76 * @param alpha0 Lower limit on the step size. 1.e-2 is the default.
77 * @param eps_subsp Lower limit on linear dependencies of basis vectors in history list. Default 1.e-4.
78 * */
79 auto& inp = dft_.ctx().cfg().vcsqnm();
80 double initial_step_size = inp.initial_step_size();
81 int nhist_max = inp.nhist_max();
82 double lattice_weight = inp.lattice_weight();
83 double alpha0 = inp.alpha0();
84 double eps_subsp = inp.eps_subsp();
85 if (compute_forces && compute_stress) {
86 geom_opt = std::make_unique<vcsqnm::PES_optimizer::periodic_optimizer>(na, lat_a, lat_b, lat_c,
87 initial_step_size, nhist_max, lattice_weight, alpha0, eps_subsp);
88 } else if (compute_forces) {
89 geom_opt = std::make_unique<vcsqnm::PES_optimizer::periodic_optimizer>(na, initial_step_size,
90 nhist_max, alpha0, eps_subsp);
91 }
92
93 bool stress_converged{true};
94 bool forces_converged{true};
95
96 for (int istep = 0; istep < max_num_steps__; istep++) {
97 RTE_OUT(dft_.ctx().out()) << "optimisation step " << istep + 1 << " out of " << max_num_steps__ << std::endl;
98
99 auto& inp = dft_.ctx().cfg().parameters();
100 bool write_state{false};
101
102 /* launch the calculation */
103 result = dft_.find(inp.density_tol(), inp.energy_tol(), dft_.ctx().cfg().iterative_solver().energy_tolerance(),
104 inp.num_dft_iter(), write_state);
105
106 rte::ostream out(dft_.ctx().out(), __func__);
107
108 if (compute_stress) {
109 dft_.stress().calc_stress_total();
110 dft_.stress().print_info(out, dft_.ctx().verbosity());
111
112 auto st = dft_.stress().stress_total();
113 double d{0};
114 for (int i = 0; i < 3; i++) {
115 for (int j = 0; j < 3; j++) {
116 stress(i, j) = -st(i, j);
117 d += std::abs(st(i, j));
118 }
119 }
120 if (d < stress_thr__) {
121 stress_converged = true;
122 } else {
123 stress_converged = false;
124 }
125
126 out << "total stress value: " << d << ", stress threshold: " << stress_thr__
127 << ", converged: " << stress_converged << std::endl;
128 }
129
130 if (compute_forces) {
131 dft_.forces().calc_forces_total();
132 dft_.forces().print_info(out, dft_.ctx().verbosity());
133
134 auto& ft = dft_.forces().forces_total();
135 double d{0};
136 for (int i = 0; i < dft_.ctx().unit_cell().num_atoms(); i++) {
137 for (int x: {0, 1, 2}) {
138 f(x, i) = ft(x, i);
139 d += std::abs(ft(x, i));
140 }
141 }
142 if (d < forces_thr__) {
143 forces_converged = true;
144 } else {
145 forces_converged = false;
146 }
147 out << "total forces value: " << d << ", forces threshold: " << forces_thr__
148 << ", converged: " << forces_converged << std::endl;
149 }
150 auto etot = result["energy"]["total"].get<double>();
151
152 if (forces_converged && stress_converged) {
153 out << "lattice relaxation is converged in " << istep << " steps" << std::endl;
154 break;
155 }
156
157 /*
158 * compute new geometry
159 */
160 if (compute_forces && compute_stress) {
161 geom_opt->step(r, etot, f, lat_a, lat_b, lat_c, stress);
162 } else if (compute_forces) {
163 geom_opt->step(r, etot, f);
164 }
165 /*
166 * update geometry
167 */
168 auto& ctx = const_cast<Simulation_context&>(dft_.ctx());
169 ctx.unit_cell().set_lattice_vectors({lat_a[0], lat_a[1], lat_a[2]},
170 {lat_b[0], lat_b[1], lat_b[2]},
171 {lat_c[0], lat_c[1], lat_c[2]});
172 for (int ia = 0; ia < na; ia++) {
173 ctx.unit_cell().atom(ia).set_position({r(0, ia), r(1, ia), r(2, ia)});
174 }
175 dft_.update();
176 ctx.unit_cell().print_geometry_info(out, ctx.verbosity());
177 }
178 if (!(forces_converged && stress_converged)) {
179 RTE_OUT(dft_.ctx().out()) << "lattice relaxation not converged" << std::endl;
180 }
181#else
182 RTE_THROW("not compiled with vcsqnm support");
183#endif
184 return result;
185 }
186};
187
188}
189
190#endif
The whole DFT ground state implementation.
json find(double density_tol, double energy_tol, double initial_tolerance, int num_dft_iter, bool write_state)
Run the SCF ground state calculation and find a total energy minimum.
Simulation_context const & ctx() const
Return reference to a simulation context.
void update()
Update the parameters after the change of lattice vectors or atomic positions.
Simulation context is a set of parameters and objects describing a single simulation.
std::ostream & out() const
Return output stream.
Contains definition and partial implementation of sirius::DFT_ground_state class.
Namespace of the SIRIUS library.
Definition: sirius.f90:5
Implementation of the vc-sqnm method. More informations about the algorithm can be found here: https:...