25#ifndef __RADIAL_SOLVER_HPP__
26#define __RADIAL_SOLVER_HPP__
149 template <relativity_t rel,
bool prevent_overflow>
151 Spline<double> const& chi_q__, std::vector<double>& p__, std::vector<double>& dpdr__,
152 std::vector<double>& q__, std::vector<double>& dqdr__)
const
155 int nr = num_points();
161 double sq_alpha_half = 0.5 / rest_energy;
163 if (rel == relativity_t::none) {
167 double ll_half = l__ * (l__ + 1) / 2.0;
170 if (rel == relativity_t::dirac) {
173 }
else if (k__ == l__ + 1) {
176 throw std::runtime_error(
"wrong k");
180 auto rel_mass = [sq_alpha_half](
double enu__,
double v__) ->
double {
182 case relativity_t::none: {
185 case relativity_t::koelling_harmon: {
186 return 1.0 + sq_alpha_half * (enu__ - v__);
188 case relativity_t::zora: {
189 return 1.0 - sq_alpha_half * v__;
191 case relativity_t::iora: {
192 double m0 = 1.0 - sq_alpha_half * v__;
193 return m0 / (1 - sq_alpha_half * enu__ / m0);
203 for (
int ir = 0; ir < nr; ir++) {
213 for (
int ir = 0; ir < nr; ir++) {
224 double v2 =
ve_(0) -
zn_ / x2;
225 double M2 = rel_mass(enu__, v2);
226 double chi_p2 = chi_p__(0);
227 double chi_q2 = chi_q__(0);
230 if (rel != relativity_t::dirac) {
232 p__[0] = 2 *
zn_ * x2;
233 q__[0] = -std::pow(
zn_, 2) * x2;
235 p__[0] = std::pow(x2, l__ + 1);
236 q__[0] = std::pow(x2, l__) * l__ / 2;
240 p__[0] = std::pow(x2, b);
262 for (
int i = 0; i < nr - 1; i++) {
265 double xinv0 = xinv2;
267 double chi_p0 = chi_p2;
268 double chi_q0 = chi_q2;
275 double h_half = h / 2;
276 double x1 = x0 + h_half;
277 double xinv1 = 1.0 / x1;
278 double chi_p1 = chi_p__(i, h_half);
279 double chi_q1 = chi_q__(i, h_half);
280 double v1 =
ve_(i, h_half) -
zn_ * xinv1;
281 double M1 = rel_mass(enu__, v1);
286 chi_p2 = chi_p__(i + 1);
287 chi_q2 = chi_q__(i + 1);
288 v2 =
ve_(i + 1) -
zn_ * xinv2;
289 M2 = rel_mass(enu__, v2);
291 if (rel == relativity_t::none || rel == relativity_t::koelling_harmon || rel == relativity_t::zora) {
293 pk[0] = 2 * M0 * q0 + p0 * xinv0;
294 qk[0] = (v0 - enu__ + ll_half / M0 / std::pow(x0, 2)) * p0 - q0 * xinv0 + chi_q0;
297 pk[1] = 2 * M1 * (q0 + qk[0] * h_half) + (p0 + pk[0] * h_half) * xinv1;
298 qk[1] = (v1 - enu__ + ll_half / M1 / std::pow(x1, 2)) * (p0 + pk[0] * h_half) -
299 (q0 + qk[0] * h_half) * xinv1 + chi_q1;
302 pk[2] = 2 * M1 * (q0 + qk[1] * h_half) + (p0 + pk[1] * h_half) * xinv1;
303 qk[2] = (v1 - enu__ + ll_half / M1 / std::pow(x1, 2)) * (p0 + pk[1] * h_half) -
304 (q0 + qk[1] * h_half) * xinv1 + chi_q1;
307 pk[3] = 2 * M2 * (q0 + qk[2] * h) + (p0 + pk[2] * h) * xinv2;
308 qk[3] = (v2 - enu__ + ll_half / M2 / std::pow(x2, 2)) * (p0 + pk[2] * h) - (q0 + qk[2] * h) * xinv2 +
311 if (rel == relativity_t::iora) {
312 double m0 = 1 - sq_alpha_half * v0;
313 double m1 = 1 - sq_alpha_half * v1;
314 double m2 = 1 - sq_alpha_half * v2;
316 double a0 = ll_half / m0 / std::pow(x0, 2);
317 double a1 = ll_half / m1 / std::pow(x1, 2);
318 double a2 = ll_half / m2 / std::pow(x2, 2);
321 pk[0] = 2 * M0 * q0 + p0 * xinv0 + chi_p0;
322 qk[0] = (v0 - enu__ + a0 - sq_alpha_half * a0 * enu__ / m0) * p0 - q0 * xinv0 + chi_q0;
325 pk[1] = 2 * M1 * (q0 + qk[0] * h_half) + (p0 + pk[0] * h_half) * xinv1 + chi_p1;
326 qk[1] = (v1 - enu__ + a1 - sq_alpha_half * a1 * enu__ / m1) * (p0 + pk[0] * h_half) -
327 (q0 + qk[0] * h_half) * xinv1 + chi_q1;
330 pk[2] = 2 * M1 * (q0 + qk[1] * h_half) + (p0 + pk[1] * h_half) * xinv1 + chi_p1;
331 qk[2] = (v1 - enu__ + a1 - sq_alpha_half * a1 * enu__ / m1) * (p0 + pk[1] * h_half) -
332 (q0 + qk[1] * h_half) * xinv1 + chi_q1;
335 pk[3] = 2 * M2 * (q0 + qk[2] * h) + (p0 + pk[2] * h) * xinv2 + chi_p2;
336 qk[3] = (v2 - enu__ + a2 - sq_alpha_half * a2 * enu__ / m2) * (p0 + pk[2] * h) -
337 (q0 + qk[2] * h) * xinv2 + chi_q2;
339 if (rel == relativity_t::dirac) {
341 pk[0] = alpha * (2 * rest_energy + enu__ - v0) * q0 - kappa * p0 * xinv0;
342 qk[0] = alpha * (v0 - enu__) * p0 + kappa * q0 * xinv0;
345 pk[1] = alpha * (2 * rest_energy + enu__ - v1) * (q0 + qk[0] * h_half) -
346 kappa * (p0 + pk[0] * h_half) * xinv1;
347 qk[1] = alpha * (v1 - enu__) * (p0 + pk[0] * h_half) + kappa * (q0 + qk[0] * h_half) * xinv1;
350 pk[2] = alpha * (2 * rest_energy + enu__ - v1) * (q0 + qk[1] * h_half) -
351 kappa * (p0 + pk[1] * h_half) * xinv1;
352 qk[2] = alpha * (v1 - enu__) * (p0 + pk[1] * h_half) + kappa * (q0 + qk[1] * h_half) * xinv1;
355 pk[3] = alpha * (2 * rest_energy + enu__ - v2) * (q0 + qk[2] * h) - kappa * (p0 + pk[2] * h) * xinv2;
356 qk[3] = alpha * (v2 - enu__) * (p0 + pk[2] * h) + kappa * (q0 + qk[2] * h) * xinv2;
359 p2 = p0 + (pk[0] + 2 * (pk[1] + pk[2]) + pk[3]) * h / 6.0;
360 q2 = q0 + (qk[0] + 2 * (qk[1] + qk[2]) + qk[3]) * h / 6.0;
364 if (std::abs(p2) > 1e4) {
367 if (!prevent_overflow || (prevent_overflow && i < idx_ctp)) {
369 if (!prevent_overflow) {
370 s <<
"unexpected overflow ";
372 s <<
"overflow before the classical turning point ";
374 s <<
"for atom type with zn = " <<
zn_ <<
", l = " << l__ <<
", enu = " << enu__ <<
", ir = " << i
375 <<
", idx_ctp: " << idx_ctp;
376 for (
int j = 0; j <= i; j++) {
394 if (prevent_overflow && last) {
396 double pmax = std::abs(p__[last]);
398 for (
int j = last; j >= 0; j--) {
399 if (std::abs(p__[j]) < pmax) {
400 pmax = std::abs(p__[j]);
409 for (
int j = last; j < nr; j++) {
417 for (
int i = 0; i < nr - 1; i++) {
418 if (p__[i] * p__[i + 1] < 0.0) {
423 for (
int i = 0; i < nr; i++) {
424 if (rel == relativity_t::none || rel == relativity_t::koelling_harmon || rel == relativity_t::zora) {
426 double M = rel_mass(enu__, V);
433 dqdr__[i] = (V - enu__ + v1) * p__[i] - q__[i] *
radial_grid_.
x_inv(i) + chi_q__(i);
435 if (rel == relativity_t::iora) {
437 double M = rel_mass(enu__, V);
438 double M0 = 1 - sq_alpha_half * V;
439 double v1 = ll_half / M0 / std::pow(
radial_grid_[i], 2);
440 double v2 = sq_alpha_half * ll_half * enu__ / std::pow(
radial_grid_[i] * M0, 2);
446 dqdr__[i] = (V - enu__ + v1 - v2) * p__[i] - q__[i] *
radial_grid_.
x_inv(i) + chi_q__(i);
448 if (rel == relativity_t::dirac) {
451 dpdr__[i] = alpha * (2 * rest_energy + enu__ - V) * q__[i] - kappa * p__[i] *
radial_grid_.
x_inv(i);
453 dqdr__[i] = alpha * (V - enu__) * p__[i] + kappa * q__[i] *
radial_grid_.
x_inv(i);
645 for (
int i = 0; i < num_points(); i++) {
651 std::tuple<int, std::vector<double>, std::vector<double>, std::vector<double>, std::vector<double>>
652 solve(
relativity_t rel__,
int dme__,
int l__,
int k__,
double enu__)
const
654 int nr = num_points();
655 std::vector<std::vector<double>> p;
656 std::vector<std::vector<double>> q;
657 std::vector<std::vector<double>> dpdr;
658 std::vector<std::vector<double>> dqdr;
665 for (
int j = 0; j <= dme__; j++) {
666 p.push_back(std::vector<double>(nr));
667 q.push_back(std::vector<double>(nr));
668 dpdr.push_back(std::vector<double>(nr));
669 dqdr.push_back(std::vector<double>(nr));
672 if (rel__ == relativity_t::none || rel__ == relativity_t::zora) {
673 for (
int i = 0; i < nr; i++) {
674 chi_q(i) = -j * p[j - 1][i];
677 }
else if (rel__ == relativity_t::koelling_harmon) {
679 double ll_half = l__ * (l__ + 1) / 2.0;
681 for (
int i = 0; i < nr; i++) {
684 double M = 1 + 0.5 * sq_alpha * (enu__ - V);
685 chi_p(i) = sq_alpha * q[j - 1][i];
686 chi_q(i) = -p[j - 1][i] * (1 + 0.5 * sq_alpha * ll_half / std::pow(M * x, 2));
689 throw std::runtime_error(
"not implemented");
691 }
else if (rel__ == relativity_t::iora) {
693 double ll_half = l__ * (l__ + 1) / 2.0;
696 for (
int i = 0; i < nr; i++) {
698 double M = 1 - 0.5 * sq_alpha * V;
700 chi_p(i) = q[j - 1][i] * sq_alpha / std::pow(1 - sq_alpha * enu__ / 2 / M, 2);
701 chi_q(i) = -p[j - 1][i] * (1 + 0.5 * sq_alpha * ll_half / std::pow(M * x, 2));
704 for (
int i = 0; i < nr; i++) {
706 double M = 1 - 0.5 * sq_alpha * V;
709 q[j - 1][i] * 2 * sq_alpha / std::pow(1 - sq_alpha * enu__ / 2 / M, 2) +
710 q[j - 2][i] * std::pow(sq_alpha, 2) / 2 / M / std::pow(1 - sq_alpha * enu__ / 2 / M, 3);
711 chi_q(i) = -p[j - 1][i] * 2 * (1 + 0.5 * sq_alpha * ll_half / std::pow(M * x, 2));
714 throw std::runtime_error(
"not implemented");
719 throw std::runtime_error(
"not implemented");
724 case relativity_t::none: {
725 nn = integrate_forward_rk4<relativity_t::none, false>(enu__, l__, 0, chi_p, chi_q, p[j], dpdr[j],
729 case relativity_t::koelling_harmon: {
730 nn = integrate_forward_rk4<relativity_t::koelling_harmon, false>(enu__, l__, 0, chi_p, chi_q, p[j],
731 dpdr[j], q[j], dqdr[j]);
734 case relativity_t::zora: {
735 nn = integrate_forward_rk4<relativity_t::zora, false>(enu__, l__, 0, chi_p, chi_q, p[j], dpdr[j],
739 case relativity_t::iora: {
740 nn = integrate_forward_rk4<relativity_t::iora, false>(enu__, l__, 0, chi_p, chi_q, p[j], dpdr[j],
745 throw std::runtime_error(
"not implemented");
750 return std::make_tuple(nn, p.back(), dpdr.back(), q.back(), dqdr.back());
781 std::vector<double>& rdudr__, std::array<double, 2>& uderiv__)
const
783 auto result = solve(rel__, dme__, l__, 0, enu__);
784 int nr = num_points();
786 auto p0 = std::get<1>(result);
787 auto p1 = std::get<2>(result);
788 auto q0 = std::get<3>(result);
789 auto q1 = std::get<4>(result);
802 uderiv__[0] = (p1.back() - p0.back() / R) / R;
806 uderiv__[1] = (sdpdr.deriv(1, nr - 1) - 2 * p1.back() / R + 2 * p0.back() / std::pow(R, 2)) / R;
808 return std::get<0>(result);
811 inline int num_points()
const
816 inline int zn()
const
821 inline double radial_grid(
int i__)
const
826 inline Radial_grid<double>
const& radial_grid()
const
856 std::vector<double> dpdr_;
860 int np = num_points();
865 std::vector<double>
p(np);
866 std::vector<double> q(np);
867 std::vector<double> dqdr(np);
868 std::vector<double> rdudr(np);
869 dpdr_ = std::vector<double>(np);
877 for (
int iter = 0; iter < 1000; iter++) {
881 case relativity_t::none: {
882 nn = integrate_forward_rk4<relativity_t::none, true>(enu_, l_, k_, chi_p, chi_q,
p, dpdr_, q, dqdr);
885 case relativity_t::koelling_harmon: {
886 nn = integrate_forward_rk4<relativity_t::koelling_harmon, true>(enu_, l_, k_, chi_p, chi_q,
p,
890 case relativity_t::zora: {
891 nn = integrate_forward_rk4<relativity_t::zora, true>(enu_, l_, k_, chi_p, chi_q,
p, dpdr_, q, dqdr);
894 case relativity_t::dirac: {
895 nn = integrate_forward_rk4<relativity_t::dirac, true>(enu_, l_, k_, chi_p, chi_q,
p, dpdr_, q,
900 throw std::runtime_error(
"not implemented");
905 s = (nn > (n_ - l_ - 1)) ? -1 : 1;
906 denu = s * std::abs(denu);
907 denu = (s != sp) ? denu * 0.5 : denu * 1.25;
917 s <<
"enu is not converged for n = " << n_ <<
" and l = " << l_ << std::endl
918 <<
"enu = " << enu_ <<
", denu = " << denu;
919 throw std::runtime_error(s.str());
923 for (
int i = 0; i < num_points(); i++) {
924 rdudr[i] = dpdr_[i] -
p[i] / radial_grid(i);
929 for (
int i = 0; i < np; i++) {
938 for (
int i = idxtp; i < np; i++) {
939 if (std::abs(
p[i]) < t1 &&
p[i - 1] *
p[i] > 0) {
949 for (
int i = 0; i < np; i++) {
953 rdudr_(i) = rdudr[i];
961 double norm =
inner(p_, p_, 0);
962 if (rel__ == relativity_t::dirac) {
963 norm +=
inner(q_, q_, 0);
966 norm = 1.0 / std::sqrt(norm);
974 for (
int i = 0; i < np - 1; i++) {
975 if (p_(i) * p_(i + 1) < 0.0) {
980 if (nn != (n_ - l_ - 1)) {
981 FILE* fout = fopen(
"p.dat",
"w");
982 for (
int ir = 0; ir < np; ir++) {
983 double x = radial_grid(ir);
984 fprintf(fout,
"%12.6f %16.8f\n", x, p_(ir));
989 s <<
"n = " << n_ << std::endl
990 <<
"l = " << l_ << std::endl
991 <<
"enu = " << enu_ << std::endl
992 <<
"wrong number of nodes : " << nn <<
" instead of " << (n_ - l_ - 1);
993 throw std::runtime_error(s.str());
996 for (
int i = 0; i < np - 1; i++) {
997 rho_(i) += std::pow(u_(i), 2);
998 if (rel__ == relativity_t::dirac) {
1006 std::vector<double>
const& v__,
double enu_start__)
1015 , rdudr_(radial_grid__)
1016 , rho_(radial_grid__)
1018 solve(rel__, enu_start__);
1021 inline double enu()
const
1044 std::vector<double>
const& dpdr()
const
1064 int np = num_points();
1069 std::vector<double> p(np);
1070 std::vector<double> q(np);
1071 std::vector<double> dpdr(np);
1072 std::vector<double> dqdr(np);
1074 double enu = enu_start__;
1082 for (
int i = 0; i < 1000; i++) {
1086 case relativity_t::none: {
1087 nnd = integrate_forward_rk4<relativity_t::none, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1090 case relativity_t::koelling_harmon: {
1091 nnd = integrate_forward_rk4<relativity_t::koelling_harmon, false>(enu, l_, 0, chi_p, chi_q, p, dpdr,
1095 case relativity_t::zora: {
1096 nnd = integrate_forward_rk4<relativity_t::zora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1099 case relativity_t::iora: {
1100 nnd = integrate_forward_rk4<relativity_t::iora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1104 throw std::runtime_error(
"not implemented");
1107 nnd -= (n_ - l_ - 1);
1109 enu = (nnd > 0) ? enu - de : enu + de;
1112 de = (nnd != nndp) ? de * 0.5 : de * 1.25;
1114 if (std::abs(de) < 1e-10) {
1120 etop_ = (!found) ? enu_start__ : enu;
1122 auto surface_deriv = [
this, &dpdr, &p]() {
1132 double sd = surface_deriv();
1137 for (
int i = 0; i < 100; i++) {
1141 case relativity_t::none: {
1142 integrate_forward_rk4<relativity_t::none, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1145 case relativity_t::koelling_harmon: {
1146 integrate_forward_rk4<relativity_t::koelling_harmon, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q,
1150 case relativity_t::zora: {
1151 integrate_forward_rk4<relativity_t::zora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1154 case relativity_t::iora: {
1155 integrate_forward_rk4<relativity_t::iora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1159 throw std::runtime_error(
"not implemented");
1162 if (surface_deriv() * sd <= 0) {
1169 double e0 = enu + de;
1171 for (
int i = 0; i < 100; i++) {
1172 enu = (e1 + e0) / 2.0;
1174 case relativity_t::none: {
1175 integrate_forward_rk4<relativity_t::none, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1178 case relativity_t::koelling_harmon: {
1179 integrate_forward_rk4<relativity_t::koelling_harmon, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q,
1183 case relativity_t::zora: {
1184 integrate_forward_rk4<relativity_t::zora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1187 case relativity_t::iora: {
1188 integrate_forward_rk4<relativity_t::iora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1192 throw std::runtime_error(
"not implemented");
1196 if (std::abs(surface_deriv()) < 1e-10) {
1200 if (surface_deriv() * sd > 0) {
1211 case relativity_t::none: {
1212 nn = integrate_forward_rk4<relativity_t::none, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1215 case relativity_t::koelling_harmon: {
1216 nn = integrate_forward_rk4<relativity_t::koelling_harmon, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q,
1220 case relativity_t::zora: {
1221 nn = integrate_forward_rk4<relativity_t::zora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1224 case relativity_t::iora: {
1225 nn = integrate_forward_rk4<relativity_t::iora, false>(enu, l_, 0, chi_p, chi_q, p, dpdr, q, dqdr);
1229 throw std::runtime_error(
"not implemented");
1233 if (nn != n_ - l_ - 1) {
1234 FILE* fout = fopen(
"p.dat",
"w");
1235 for (
int ir = 0; ir < np; ir++) {
1236 double x = radial_grid(ir);
1237 fprintf(fout,
"%16.8f %16.8f %16.8f\n", x, p[ir], q[ir]);
1242 std::stringstream s;
1243 s <<
"wrong number of nodes: " << nn <<
" instead of " << n_ - l_ - 1 << std::endl
1244 <<
"n: " << n_ <<
", l: " << l_ << std::endl
1245 <<
"etop: " << etop_ <<
" ebot: " << ebot_ << std::endl
1246 <<
"initial surface derivative: " << sd;
1248 throw std::runtime_error(s.str());
1251 enu_ = (ebot_ + etop_) / 2.0;
1257 std::vector<double>
const& v__,
double enu_start__)
1263 find_enu(rel__, enu_start__);
1266 inline double enu()
const
1271 inline double ebot()
const
1276 inline double etop()
const
Spline< double > const & u() const
Return radial function.
Spline< double > const & p() const
Return radial function multiplied by x.
Spline< double > const & rho() const
Return charge density, corresponding to a radial solution.
double enu_tolerance_
Tolerance of bound state energy.
Enu_finder(relativity_t rel__, int zn__, int n__, int l__, Radial_grid< double > const &radial_grid__, std::vector< double > const &v__, double enu_start__)
Constructor.
T last() const
Last point of the grid.
T dx(const int i) const
Return .
T x_inv(const int i) const
Return .
int num_points() const
Number of grid points.
Finds a solution to radial Schrodinger, Koelling-Harmon or Dirac equation.
Radial_grid< double > const & radial_grid_
Radial grid.
int zn_
Positive charge of the nucleus.
int solve(relativity_t rel__, int dme__, int l__, double enu__, std::vector< double > &p__, std::vector< double > &rdudr__, std::array< double, 2 > &uderiv__) const
Integrates the radial equation for a given energy and finds the m-th energy derivative of the radial ...
Spline< double > ve_
Electronic part of potential.
int integrate_forward_rk4(double enu__, int l__, int k__, Spline< double > const &chi_p__, Spline< double > const &chi_q__, std::vector< double > &p__, std::vector< double > &dpdr__, std::vector< double > &q__, std::vector< double > &dqdr__) const
Integrate system of two first-order differential equations forward starting from the origin.
Spline< T, U > & interpolate()
std::enable_if_t< std::is_same< T, real_type< F > >::value, void > inner(::spla::Context &spla_ctx__, sddk::memory_t mem__, spin_range spins__, W const &wf_i__, band_range br_i__, Wave_functions< T > const &wf_j__, band_range br_j__, la::dmatrix< F > &result__, int irow0__, int jcol0__)
Compute inner product between the two sets of wave-functions.
Namespace of the SIRIUS library.
relativity_t
Type of relativity treatment in the case of LAPW.
const double speed_of_light
NIST value for the inverse fine structure (http://physics.nist.gov/cuu/Constants/index....
Contains definition and partial implementation of sirius::Spline class.
Contains typedefs, enums and simple descriptors.