SIRIUS 7.5.0
Electronic structure library and applications
spline.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2017 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 spline.hpp
21 *
22 * \brief Contains definition and partial implementation of sirius::Spline class.
23 */
24
25#ifndef __SPLINE_HPP__
26#define __SPLINE_HPP__
27
29
30namespace sirius {
31
32/// Cubic spline with a not-a-knot boundary conditions.
33/** The following convention for spline coefficients is used: for \f$ x \f$ in
34 * \f$ [x_i, x_{i+1}] \f$ the value of the spline is equal to
35 * \f$ a_i + b_i(x - x_i) + c_i(x - x_i)^2 + d_i(x - x_i)^3 \f$.
36 *
37 * Suppose we have \f$ n \f$ value points \f$ y_i = f(x_i) \f$ and \f$ n - 1 \f$ segments:
38 * \f[
39 * S_i(x) = y_i + b_i(x - x_i) + c_i(x - x_i)^2 + d_i(x - x_i)^3
40 * \f]
41 * Segment derivatives:
42 * \f[
43 * \begin{eqnarray}
44 * S_i'(x) &=& b_i + 2c_i(x - x_i) + 3d_i(x - x_i)^2 \\
45 * S_i''(x) &=& 2c_i + 6d_i(x-x_i) \\
46 * S_i'''(x) &=& 6d_i
47 * \end{eqnarray}
48 * \f]
49 * The following substitutions are made:
50 * \f[
51 * m_i = 2 c_i \\
52 * h_i = x_{i+1} - x_i \\
53 * y_i' = \frac{y_{i+1} - y_i}{h_i}
54 * \f]
55 * Now we can equate the derivatives at the end points of segments. From the 3rd derivative we get
56 * \f[
57 * d_i = \frac{1}{6h_i}(m_{i+1} - m_i)
58 * \f]
59 * From the 1st derivative we get
60 * \f[
61 * b_i = y_i' - \frac{h_i}{2} m_i - \frac{h_i}{6}(m_{i+1} - m_i)
62 * \f]
63 * Using 2nd derivative condition we get
64 * \f[
65 * h_i m_i + 2(h_{i} + h_{i+1})m_{i+1} + h_{i+1}m_{i+2} = 6(y_{i+1}' - y_{i}')
66 * \f]
67 * So far we got \f$ n - 3 \f$ equations for \f$ n - 1 \f$ coefficients \f$ m_i \f$. We need two extra conditions.
68 * Not-a-knot boundary condition (counting segments and points from 1):
69 * \f[
70 * S_1'''(x_2) = S_2'''(x_2) \longrightarrow d_1 = d_2 \\
71 * S_{n-2}'''(x_{n-1}) = S_{n-1}'''(x_{n-1}) \longrightarrow d_{n-2} = d_{n-1}
72 * \f]
73 */
74template <typename T, typename U = double>
75class Spline : public Radial_grid<U>
76{
77 private:
78 /// Array of spline coefficients.
80 /* forbid copy constructor */
81 Spline(Spline<T, U> const& src__) = delete;
82 /* forbid assignment operator */
83 Spline<T, U>& operator=(Spline<T, U> const& src__) = delete;
84 /// Solver tridiagonal system of linear equaitons.
85 int solve(T* dl, T* d, T* du, T* b, int n)
86 {
87 for (int i = 0; i < n - 1; i++) {
88 if (std::abs(dl[i]) == 0) {
89 if (std::abs(d[i]) == 0) {
90 return i + 1;
91 }
92 } else if (std::abs(d[i]) >= std::abs(dl[i])) {
93 T mult = dl[i] / d[i];
94 d[i + 1] -= mult * du[i];
95 b[i + 1] -= mult * b[i];
96 if (i < n - 2) {
97 dl[i] = 0;
98 }
99 } else {
100 T mult = d[i] / dl[i];
101 d[i] = dl[i];
102 T tmp = d[i + 1];
103 d[i + 1] = du[i] - mult * tmp;
104 if (i < n - 2) {
105 dl[i] = du[i + 1];
106 du[i + 1] = -mult * dl[i];
107 }
108 du[i] = tmp;
109 tmp = b[i];
110 b[i] = b[i + 1];
111 b[i + 1] = tmp - mult * b[i + 1];
112 }
113 }
114 if (std::abs(d[n - 1]) == 0) {
115 return n;
116 }
117 b[n - 1] /= d[n - 1];
118 if (n > 1) {
119 b[n - 2] = (b[n - 2] - du[n - 2] * b[n - 1]) / d[n - 2];
120 }
121 for (int i = n - 3; i >= 0; i--) {
122 b[i] = (b[i] - du[i] * b[i + 1] - dl[i] * b[i + 2]) / d[i];
123 }
124 return 0;
125 }
126 /// Init the underlying radial grid.
127 void init_grid(Radial_grid<U> const& radial_grid__)
128 {
129 /* copy the grid points */
130 this->x_ = sddk::mdarray<U, 1>(radial_grid__.num_points());
131 sddk::copy(radial_grid__.x(), this->x_);
132 this->init();
133 }
134
135 public:
136 /// Default constructor.
138 {
139 }
140 /// Constructor of a new empty spline.
141 Spline(Radial_grid<U> const& radial_grid__)
142 {
143 init_grid(radial_grid__);
145 coeffs_.zero();
146 }
147 /// Constructor of a spline from a function.
148 Spline(Radial_grid<U> const& radial_grid__, std::function<T(U)> f__)
149 {
150 init_grid(radial_grid__);
152 for (int i = 0; i < this->num_points(); i++) {
153 coeffs_(i, 0) = f__(this->x(i));
154 }
155 interpolate();
156 }
157 /// Constructor of a spline from a list of values.
158 Spline(Radial_grid<U> const& radial_grid__, std::vector<T> const& y__)
159 {
160 init_grid(radial_grid__);
161 assert(static_cast<int>(y__.size()) <= this->num_points());
163 coeffs_.zero();
164 int i{0};
165 for (auto e : y__) {
166 this->coeffs_(i++, 0) = e;
167 }
168 interpolate();
169 }
170
171 Spline(Spline<T, U>&& src__) = default;
172
173 Spline<T, U>& operator=(Spline<T, U>&& src__) = default;
174
175 Spline<T, U>& operator=(std::function<T(U)> f__)
176 {
177 for (int ir = 0; ir < this->num_points(); ir++) {
178 coeffs_(ir, 0) = f__(this->x(ir));
179 }
180 return interpolate();
181 }
182
183 Spline<T, U>& operator=(std::vector<T> const& y__)
184 {
185 assert(static_cast<int>(y__.size()) <= this->num_points());
186 coeffs_.zero();
187 int i{0};
188 for (auto e : y__) {
189 this->coeffs_(i++, 0) = e;
190 }
191 return interpolate();
192 }
193
194 /// Get the reference to a value at the point x[i].
195 inline T& operator()(const int i)
196 {
197 return coeffs_(i, 0);
198 }
199
200 /// Get value at the point x[i].
201 inline T const& operator()(const int i) const
202 {
203 return coeffs_(i, 0);
204 }
205
206 /// Get value at the point x[i] + dx.
207 inline T operator()(const int i, U dx) const
208 {
209 assert(i >= 0);
210 assert(i < this->num_points() - 1);
211 assert(dx >= 0);
212 return coeffs_(i, 0) + dx * (coeffs_(i, 1) + dx * (coeffs_(i, 2) + dx * coeffs_(i, 3)));
213 }
214
215 /// Compute value at any point.
216 inline T at_point(U x) const
217 {
218 int j = this->index_of(x);
219 if (j == -1) {
220 std::stringstream s;
221 s << "spline::at_point() index of point is not found\n"
222 << " x : " << x << "\n"
223 << " first point : " << this->first() << "\n"
224 << " last point : " << this->last();
225 throw std::runtime_error(s.str());
226 }
227 U dx = x - (*this)[j];
228 return (*this)(j, dx);
229 }
230
231 inline T deriv(const int dm, const int i, const U dx) const
232 {
233 assert(i >= 0);
234 assert(i < this->num_points() - 1);
235 assert(dx >= 0);
236
237 T result = 0;
238 switch (dm) {
239 case 0: {
240 result = coeffs_(i, 0) + dx * (coeffs_(i, 1) + dx * (coeffs_(i, 2) + dx * coeffs_(i, 3)));
241 break;
242 }
243 case 1: {
244 result = coeffs_(i, 1) + (coeffs_(i, 2) * 2.0 + coeffs_(i, 3) * dx * 3.0) * dx;
245 break;
246 }
247 case 2: {
248 result = coeffs_(i, 2) * 2.0 + coeffs_(i, 3) * dx * 6.0;
249 break;
250 }
251 case 3: {
252 result = coeffs_(i, 3) * 6.0;
253 break;
254 }
255 default: {
256 throw std::runtime_error("wrong order of derivative");
257 break;
258 }
259 }
260 return result;
261 }
262
263 inline T deriv(int dm, int i) const
264 {
265 assert(i >= 0);
266 assert(i < this->num_points());
267
268 if (i == this->num_points() - 1) {
269 return deriv(dm, i - 1, this->dx(i - 1));
270 } else {
271 return deriv(dm, i, 0);
272 }
273 }
274
276 {
277 /* number of segments; in principle we have n-1 segments, but the equations for spline are built in such a
278 way that one extra m_i coefficient of a segment is necessary */
279 int ns = this->num_points();
280 assert(ns >= 4);
281 /* lower diagonal (use coeffs as temporary storage) */
282 T* dl = coeffs_.at(sddk::memory_t::host, 0, 1);
283 /* main diagonal */
284 T* d = coeffs_.at(sddk::memory_t::host, 0, 2);
285 /* upper diagonal */
286 T* du = coeffs_.at(sddk::memory_t::host, 0, 3);
287 /* m_i = 2 c_i */
288 std::vector<T> m(ns);
289 /* derivatives of function */
290 std::vector<T> dy(ns - 1);
291 for (int i = 0; i < ns - 1; i++) {
292 dy[i] = (coeffs_(i + 1, 0) - coeffs_(i, 0)) / this->dx(i);
293 }
294 /* setup "B" vector of AX=B equation */
295 for (int i = 0; i < ns - 2; i++) {
296 m[i + 1] = (dy[i + 1] - dy[i]) * 6.0;
297 }
298 /* this is derived from n-a-k boundary condition */
299 m[0] = m[1];
300 m[ns - 1] = m[ns - 2];
301
302 /* main diagonal of "A" matrix */
303 for (int i = 0; i < ns - 2; i++) {
304 d[i + 1] = static_cast<T>(this->x(i + 2) - this->x(i)) * 2.0;
305 }
306 /* subdiagonals of "A" matrix */
307 for (int i = 0; i < ns - 1; i++) {
308 du[i] = this->dx(i);
309 dl[i] = this->dx(i);
310 }
311
312 /* last part of n-a-k boundary condition */
313 U h0 = this->dx(0);
314 U h1 = this->dx(1);
315 d[0] = h0 - (h1 / h0) * h1;
316 du[0] = h1 * ((h1 / h0) + 1) + 2 * (h0 + h1);
317
318 h0 = this->dx(ns - 2);
319 h1 = this->dx(ns - 3);
320 d[ns - 1] = h0 - (h1 / h0) * h1;
321 dl[ns - 2] = h1 * ((h1 / h0) + 1) + 2 * (h0 + h1);
322
323 ///* this should be boundary conditions for natural spline (with zero second derivatives at boundaries) */
324 // m[0] = m[ns-1] = 0;
325 // du[0] = 0;
326 // dl[ns - 2] = 0;
327 // d[0] = d[ns-1] = 1;
328
329 /* solve tridiagonal system */
330 // int info = linalg<device_t::CPU>::gtsv(ns, 1, &dl[0], &d[0], &du[0], &m[0], ns);
331 int info = solve(dl, d, du, &m[0], ns);
332
333 if (info) {
334 std::stringstream s;
335 s << "[sirius::Spline::interpolate] error in tridiagonal solver: " << info;
336 throw std::runtime_error(s.str());
337 }
338
339 for (int i = 0; i < ns - 1; i++) {
340 /* this is c_i coefficient */
341 coeffs_(i, 2) = m[i] / 2.0;
342 /* this is why one extra segment was considered: we need m_{i+1} */
343 T t = (m[i + 1] - m[i]) / 6.0;
344 /* b_i coefficient */
345 coeffs_(i, 1) = dy[i] - (coeffs_(i, 2) + t) * this->dx(i);
346 /* d_i coefficient */
347 coeffs_(i, 3) = t / this->dx(i);
348 }
349 coeffs_(ns - 1, 1) = 0;
350 coeffs_(ns - 1, 2) = 0;
351 coeffs_(ns - 1, 3) = 0;
352
353 return *this;
354 }
355
356 /// Integrate spline with r^m prefactor.
357 /**
358 Derivation for r^2 prefactor is based on the following Mathematica notebook:
359 \verbatim
360 In[26]:= result =
361 FullSimplify[
362 Integrate[
363 x^(2)*(a0 + a1*(x - x0) + a2*(x - x0)^2 + a3*(x - x0)^3), {x, x0,
364 x1}],
365 Assumptions -> {Element[{x0, x1}, Reals], x1 > x0 > 0}]
366
367
368 Out[26]= 1/60 (20 a0 (-x0^3 + x1^3) +
369 5 a1 (x0^4 - 4 x0 x1^3 + 3 x1^4) + (x0 -
370 x1)^3 (-2 a2 (x0^2 + 3 x0 x1 + 6 x1^2) +
371 a3 (x0 - x1) (x0^2 + 4 x0 x1 + 10 x1^2)))
372
373 In[27]:= r = Expand[result] /. {x1 -> x0 + dx}
374
375 Out[27]= -((a0 x0^3)/3) + (a1 x0^4)/12 - (a2 x0^5)/30 + (
376 a3 x0^6)/60 + 1/3 a0 (dx + x0)^3 - 1/3 a1 x0 (dx + x0)^3 +
377 1/3 a2 x0^2 (dx + x0)^3 - 1/3 a3 x0^3 (dx + x0)^3 +
378 1/4 a1 (dx + x0)^4 - 1/2 a2 x0 (dx + x0)^4 +
379 3/4 a3 x0^2 (dx + x0)^4 + 1/5 a2 (dx + x0)^5 -
380 3/5 a3 x0 (dx + x0)^5 + 1/6 a3 (dx + x0)^6
381
382 In[34]:= Collect[r, dx, Simplify]
383
384 Out[34]= (a3 dx^6)/6 + a0 dx x0^2 + 1/2 dx^2 x0 (2 a0 + a1 x0) +
385 1/5 dx^5 (a2 + 2 a3 x0) + 1/3 dx^3 (a0 + x0 (2 a1 + a2 x0)) +
386 1/4 dx^4 (a1 + x0 (2 a2 + a3 x0))
387
388 In[28]:= r1 = Collect[r/dx, dx, Simplify]
389
390 Out[28]= (a3 dx^5)/6 + a0 x0^2 + 1/2 dx x0 (2 a0 + a1 x0) +
391 1/5 dx^4 (a2 + 2 a3 x0) + 1/3 dx^2 (a0 + x0 (2 a1 + a2 x0)) +
392 1/4 dx^3 (a1 + x0 (2 a2 + a3 x0))
393
394 In[29]:= r2 = Collect[(r1 - a0*x0^2)/dx, dx, Simplify]
395
396 Out[29]= (a3 dx^4)/6 + 1/2 x0 (2 a0 + a1 x0) +
397 1/5 dx^3 (a2 + 2 a3 x0) + 1/3 dx (a0 + x0 (2 a1 + a2 x0)) +
398 1/4 dx^2 (a1 + x0 (2 a2 + a3 x0))
399
400 In[30]:= r3 = Collect[(r2 - 1/2 x0 (2 a0 + a1 x0))/dx, dx, Simplify]
401
402 Out[30]= (a3 dx^3)/6 + 1/5 dx^2 (a2 + 2 a3 x0) +
403 1/3 (a0 + x0 (2 a1 + a2 x0)) + 1/4 dx (a1 + x0 (2 a2 + a3 x0))
404
405 In[31]:= r4 =
406 Collect[(r3 - 1/3 (a0 + x0 (2 a1 + a2 x0)))/dx, dx, Simplify]
407
408 Out[31]= (a3 dx^2)/6 + 1/5 dx (a2 + 2 a3 x0) +
409 1/4 (a1 + x0 (2 a2 + a3 x0))
410
411 In[32]:= r5 =
412 Collect[(r4 - 1/4 (a1 + x0 (2 a2 + a3 x0)))/dx, dx, Simplify]
413
414 Out[32]= (a3 dx)/6 + 1/5 (a2 + 2 a3 x0)
415
416 In[33]:= r6 = Collect[(r5 - 1/5 (a2 + 2 a3 x0))/dx, dx, Simplify]
417
418 Out[33]= a3/6
419
420 \endverbatim
421 */
422 T integrate(std::vector<T>& g__, int m__) const
423 {
424 g__ = std::vector<T>(this->num_points());
425 g__[0] = 0.0;
426
427 switch (m__) {
428 case 0: {
429 T t = 1.0 / 3.0;
430 for (int i = 0; i < this->num_points() - 1; i++) {
431 U dx = this->dx(i);
432 g__[i + 1] = g__[i] + (((coeffs_(i, 3) * dx * 0.25 + coeffs_(i, 2) * t) * dx + coeffs_(i, 1) * 0.5) * dx + coeffs_(i, 0)) * dx;
433 }
434 break;
435 }
436 case 2: {
437 for (int i = 0; i < this->num_points() - 1; i++) {
438 U x0 = this->x(i);
439 U dx = this->dx(i);
440 T a0 = coeffs_(i, 0);
441 T a1 = coeffs_(i, 1);
442 T a2 = coeffs_(i, 2);
443 T a3 = coeffs_(i, 3);
444
445 T val = dx * (dx * (dx * (dx * (dx * (dx * a3 / 6.0 + (a2 + 2.0 * a3 * x0) / 5.0) +
446 (a1 + x0 * (2.0 * a2 + a3 * x0)) / 4.0) + (a0 + x0 * (2.0 * a1 + a2 * x0)) / 3.0) +
447 x0 * (2.0 * a0 + a1 * x0) / 2.0) + a0 * x0 * x0);
448
449 g__[i + 1] = g__[i] + val;
450 }
451 break;
452 }
453 case -1: {
454 for (int i = 0; i < this->num_points() - 1; i++) {
455 U x0 = this->x(i);
456 U x1 = this->x(i + 1);
457 U dx = this->dx(i);
458 T a0 = coeffs_(i, 0);
459 T a1 = coeffs_(i, 1);
460 T a2 = coeffs_(i, 2);
461 T a3 = coeffs_(i, 3);
462
463 // obtained with the following Mathematica code:
464 // FullSimplify[Integrate[x^(-1)*(a0+a1*(x-x0)+a2*(x-x0)^2+a3*(x-x0)^3),{x,x0,x1}],
465 // Assumptions->{Element[{x0,x1},Reals],x1>x0>0}]
466 g__[i + 1] = g__[i] + (dx / 6.0) * (6.0 * a1 + x0 * (-9.0 * a2 + 11.0 * a3 * x0) + x1 * (3.0 * a2 - 7.0 * a3 * x0 + 2.0 * a3 * x1)) +
467 (-a0 + x0 * (a1 + x0 * (-a2 + a3 * x0))) * std::log(x0 / x1);
468 }
469 break;
470 }
471 case -2: {
472 for (int i = 0; i < this->num_points() - 1; i++) {
473 U x0 = this->x(i);
474 U x1 = this->x(i + 1);
475 U dx = this->dx(i);
476 T a0 = coeffs_(i, 0);
477 T a1 = coeffs_(i, 1);
478 T a2 = coeffs_(i, 2);
479 T a3 = coeffs_(i, 3);
480
481 // obtained with the following Mathematica code:
482 // FullSimplify[Integrate[x^(-2)*(a0+a1*(x-x0)+a2*(x-x0)^2+a3*(x-x0)^3),{x,x0,x1}],
483 // Assumptions->{Element[{x0,x1},Reals],x1>x0>0}]
484 // g__[i + 1] = g__[i] + (((x0 - x1) * (-2.0 * a0 + x0 * (2.0 * a1 - 2.0 * a2 * (x0 + x1) +
485 // a3 * (2.0 * std::pow(x0, 2) + 5.0 * x0 * x1 - std::pow(x1, 2)))) +
486 // 2.0 * x0 * (a1 + x0 * (-2.0 * a2 + 3.0 * a3 * x0)) * x1 * std::log(x1 / x0)) /
487 // (2.0 * x0 * x1));
488 g__[i + 1] =
489 g__[i] +
490 (a2 * dx - 5.0 * a3 * x0 * dx / 2.0 - a1 * (dx / x1) + a0 * (dx / x0 / x1) + (x0 / x1) * dx * (a2 - a3 * x0) + a3 * x1 * dx / 2.0) +
491 (a1 + x0 * (-2.0 * a2 + 3.0 * a3 * x0)) * std::log(x1 / x0);
492 }
493 break;
494 }
495 case -3: {
496 for (int i = 0; i < this->num_points() - 1; i++) {
497 U x0 = this->x(i);
498 U x1 = this->x(i + 1);
499 U dx = this->dx(i);
500 T a0 = coeffs_(i, 0);
501 T a1 = coeffs_(i, 1);
502 T a2 = coeffs_(i, 2);
503 T a3 = coeffs_(i, 3);
504
505 // obtained with the following Mathematica code:
506 // FullSimplify[Integrate[x^(-3)*(a0+a1*(x-x0)+a2*(x-x0)^2+a3*(x-x0)^3),{x,x0,x1}],
507 // Assumptions->{Element[{x0,x1},Reals],x1>x0>0}]
508 // g__[i + 1] = g__[i] + (-((x0 - x1) * (a0 * (x0 + x1) + x0 * (a1 * (-x0 + x1) +
509 // x0 * (a2 * x0 - a3 * std::pow(x0, 2) - 3.0 * a2 * x1 + 5.0 * a3 * x0 * x1 +
510 // 2.0 * a3 * std::pow(x1, 2)))) + 2.0 * std::pow(x0, 2) * (a2 - 3.0 * a3 * x0) * std::pow(x1, 2) *
511 // std::log(x0 / x1)) / (2.0 * std::pow(x0, 2) * std::pow(x1, 2)));
512 g__[i + 1] =
513 g__[i] +
514 dx *
515 (a0 * (x0 + x1) +
516 x0 * (a1 * dx + x0 * (a2 * x0 - a3 * std::pow(x0, 2) - 3.0 * a2 * x1 + 5.0 * a3 * x0 * x1 + 2.0 * a3 * std::pow(x1, 2)))) /
517 std::pow(x0 * x1, 2) / 2.0 +
518 (-a2 + 3.0 * a3 * x0) * std::log(x0 / x1);
519 }
520 break;
521 }
522 case -4: {
523 for (int i = 0; i < this->num_points() - 1; i++) {
524 U x0 = this->x(i);
525 U x1 = this->x(i + 1);
526 U dx = this->dx(i);
527 T a0 = coeffs_(i, 0);
528 T a1 = coeffs_(i, 1);
529 T a2 = coeffs_(i, 2);
530 T a3 = coeffs_(i, 3);
531
532 // obtained with the following Mathematica code:
533 // FullSimplify[Integrate[x^(-4)*(a0+a1*(x-x0)+a2*(x-x0)^2+a3*(x-x0)^3),{x,x0,x1}],
534 // Assumptions->{Element[{x0,x1},Reals],x1>x0>0}]
535 // g__[i + 1] = g__[i] + ((2.0 * a0 * (-std::pow(x0, 3) + std::pow(x1, 3)) +
536 // x0 * (x0 - x1) * (a1 * (x0 - x1) * (2.0 * x0 + x1) +
537 // x0 * (-2.0 * a2 * std::pow(x0 - x1, 2) + a3 * x0 * (2.0 * std::pow(x0, 2) - 7.0 * x0 * x1 +
538 // 11.0 * std::pow(x1, 2)))) + 6.0 * a3 * std::pow(x0 * x1, 3) * std::log(x1 / x0)) /
539 // (6.0 * std::pow(x0 * x1, 3)));
540 g__[i + 1] = g__[i] +
541 (2.0 * a0 * (-std::pow(x0, 3) + std::pow(x1, 3)) -
542 x0 * dx *
543 (-a1 * dx * (2.0 * x0 + x1) +
544 x0 * (-2.0 * a2 * std::pow(dx, 2) + a3 * x0 * (2.0 * std::pow(x0, 2) - 7.0 * x0 * x1 + 11.0 * std::pow(x1, 2))))) /
545 std::pow(x0 * x1, 3) / 6.0 +
546 a3 * std::log(x1 / x0);
547 }
548 break;
549 }
550 default: {
551 for (int i = 0; i < this->num_points() - 1; i++) {
552 U x0 = this->x(i);
553 U x1 = this->x(i + 1);
554 T a0 = coeffs_(i, 0);
555 T a1 = coeffs_(i, 1);
556 T a2 = coeffs_(i, 2);
557 T a3 = coeffs_(i, 3);
558
559 // obtained with the following Mathematica code:
560 // FullSimplify[Integrate[x^(m)*(a0+a1*(x-x0)+a2*(x-x0)^2+a3*(x-x0)^3),{x,x0,x1}],
561 // Assumptions->{Element[{x0,x1},Reals],x1>x0>0}]
562 g__[i + 1] =
563 g__[i] +
564 (std::pow(x0, 1 + m__) * (-(a0 * double((2 + m__) * (3 + m__) * (4 + m__))) +
565 x0 * (a1 * double((3 + m__) * (4 + m__)) - 2.0 * a2 * double(4 + m__) * x0 + 6.0 * a3 * std::pow(x0, 2)))) /
566 double((1 + m__) * (2 + m__) * (3 + m__) * (4 + m__)) +
567 std::pow(x1, 1 + m__) *
568 ((a0 - x0 * (a1 + x0 * (-a2 + a3 * x0))) / double(1 + m__) + ((a1 + x0 * (-2.0 * a2 + 3.0 * a3 * x0)) * x1) / double(2 + m__) +
569 ((a2 - 3.0 * a3 * x0) * std::pow(x1, 2)) / double(3 + m__) + (a3 * std::pow(x1, 3)) / double(4 + m__));
570 }
571 break;
572 }
573 }
574
575 return g__.back();
576 }
577
578 /// Integrate spline with r^m prefactor.
579 T integrate(int m__) const
580 {
581 std::vector<T> g;
582 return integrate(g, m__);
583 }
584
585 inline void scale(double a__)
586 {
587 for (int i = 0; i < this->num_points(); i++) {
588 coeffs_(i, 0) *= a__;
589 coeffs_(i, 1) *= a__;
590 coeffs_(i, 2) *= a__;
591 coeffs_(i, 3) *= a__;
592 }
593 }
594
595 inline std::array<T, 4> coeffs(int i__) const
596 {
597 return {coeffs_(i__, 0), coeffs_(i__, 1), coeffs_(i__, 2), coeffs_(i__, 3)};
598 }
599
600 inline sddk::mdarray<T, 2> const& coeffs() const
601 {
602 return coeffs_;
603 }
604
605 //void copy_to_device()
606 //{
607 // // Radial_grid<U>::copy_to_device();
608 // coeffs_.allocate(sddk::memory_t::device);
609 // coeffs_.template copy<memory_t::host, memory_t::device>();
610 //}
611
612 std::vector<T> values() const
613 {
614 std::vector<T> val(this->num_points());
615 for (int ir = 0; ir < this->num_points(); ir++) {
616 val[ir] = coeffs_(ir, 0);
617 }
618 return val;
619 }
620};
621
622template <typename T, typename U = double>
623inline Spline<T, U> operator*(Spline<T, U> const& a__, Spline<T, U> const& b__)
624{
625 Spline<T> s12(reinterpret_cast<Radial_grid<U> const&>(a__));
626
627 auto& coeffs_a = a__.coeffs();
628 auto& coeffs_b = b__.coeffs();
629 auto& coeffs = const_cast<sddk::mdarray<T, 2>&>(s12.coeffs());
630
631 for (int ir = 0; ir < a__.num_points(); ir++) {
632 coeffs(ir, 0) = coeffs_a(ir, 0) * coeffs_b(ir, 0);
633 coeffs(ir, 1) = coeffs_a(ir, 1) * coeffs_b(ir, 0) + coeffs_a(ir, 0) * coeffs_b(ir, 1);
634 coeffs(ir, 2) = coeffs_a(ir, 2) * coeffs_b(ir, 0) + coeffs_a(ir, 1) * coeffs_b(ir, 1) + coeffs_a(ir, 0) * coeffs_b(ir, 2);
635 coeffs(ir, 3) =
636 coeffs_a(ir, 3) * coeffs_b(ir, 0) + coeffs_a(ir, 2) * coeffs_b(ir, 1) + coeffs_a(ir, 1) * coeffs_b(ir, 2) + coeffs_a(ir, 0) * coeffs_b(ir, 3);
637 }
638
639 return s12;
640}
641
642#ifdef SIRIUS_GPU
643// extern "C" double spline_inner_product_gpu_v2(int size__,
644// double const* x__,
645// double const* dx__,
646// double const* f__,
647// double const* g__,
648// double* d_buf__,
649// double* h_buf__,
650// int stream_id__);
651//
652extern "C" void spline_inner_product_gpu_v3(int const* idx_ri__, int num_ri__, int num_points__, double const* x__, double const* dx__, double const* f__,
653 double const* g__, double* result__);
654#endif
655
656template <typename T>
657T inner(Spline<T> const& f__, Spline<T> const& g__, int m__, int num_points__)
658{
659 T result = 0;
660
661 switch (m__) {
662 case 0: {
663 for (int i = 0; i < num_points__ - 1; i++) {
664 double dx = f__.dx(i);
665
666 auto f = f__.coeffs(i);
667 auto g = g__.coeffs(i);
668
669 T faga = f[0] * g[0];
670 T fdgd = f[3] * g[3];
671
672 T k1 = f[0] * g[1] + f[1] * g[0];
673 T k2 = f[2] * g[0] + f[1] * g[1] + f[0] * g[2];
674 T k3 = f[0] * g[3] + f[1] * g[2] + f[2] * g[1] + f[3] * g[0];
675 T k4 = f[1] * g[3] + f[2] * g[2] + f[3] * g[1];
676 T k5 = f[2] * g[3] + f[3] * g[2];
677
678 result += dx * (faga + dx * (k1 / 2.0 + dx * (k2 / 3.0 + dx * (k3 / 4.0 + dx * (k4 / 5.0 + dx * (k5 / 6.0 + dx * fdgd / 7.0))))));
679 }
680 break;
681 }
682 case 1: {
683 for (int i = 0; i < num_points__ - 1; i++) {
684 double x0 = f__[i];
685 double dx = f__.dx(i);
686
687 auto f = f__.coeffs(i);
688 auto g = g__.coeffs(i);
689
690 T faga = f[0] * g[0];
691 T fdgd = f[3] * g[3];
692
693 T k1 = f[0] * g[1] + f[1] * g[0];
694 T k2 = f[2] * g[0] + f[1] * g[1] + f[0] * g[2];
695 T k3 = f[0] * g[3] + f[1] * g[2] + f[2] * g[1] + f[3] * g[0];
696 T k4 = f[1] * g[3] + f[2] * g[2] + f[3] * g[1];
697 T k5 = f[2] * g[3] + f[3] * g[2];
698
699 result +=
700 dx * ((faga * x0) +
701 dx * ((faga + k1 * x0) / 2.0 +
702 dx * ((k1 + k2 * x0) / 3.0 +
703 dx * ((k2 + k3 * x0) / 4.0 +
704 dx * ((k3 + k4 * x0) / 5.0 + dx * ((k4 + k5 * x0) / 6.0 + dx * ((k5 + fdgd * x0) / 7.0 + dx * fdgd / 8.0)))))));
705 }
706 break;
707 }
708 case 2: {
709 for (int i = 0; i < num_points__ - 1; i++) {
710 double x0 = f__[i];
711 double dx = f__.dx(i);
712
713 auto f = f__.coeffs(i);
714 auto g = g__.coeffs(i);
715
716 T k0 = f[0] * g[0];
717 T k1 = f[3] * g[1] + f[2] * g[2] + f[1] * g[3];
718 T k2 = f[3] * g[0] + f[2] * g[1] + f[1] * g[2] + f[0] * g[3];
719 T k3 = f[2] * g[0] + f[1] * g[1] + f[0] * g[2];
720 T k4 = f[3] * g[2] + f[2] * g[3];
721 T k5 = f[1] * g[0] + f[0] * g[1];
722 T k6 = f[3] * g[3]; // 25 OPS
723
724 T r1 = k4 * 0.125 + k6 * x0 * 0.25;
725 T r2 = (k1 + x0 * (2.0 * k4 + k6 * x0)) * 0.14285714285714285714;
726 T r3 = (k2 + x0 * (2.0 * k1 + k4 * x0)) * 0.16666666666666666667;
727 T r4 = (k3 + x0 * (2.0 * k2 + k1 * x0)) * 0.2;
728 T r5 = (k5 + x0 * (2.0 * k3 + k2 * x0)) * 0.25;
729 T r6 = (k0 + x0 * (2.0 * k5 + k3 * x0)) * 0.33333333333333333333;
730 T r7 = (x0 * (2.0 * k0 + x0 * k5)) * 0.5;
731
732 T v = dx * k6 * 0.11111111111111111111;
733 v = dx * (r1 + v);
734 v = dx * (r2 + v);
735 v = dx * (r3 + v);
736 v = dx * (r4 + v);
737 v = dx * (r5 + v);
738 v = dx * (r6 + v);
739 v = dx * (r7 + v);
740
741 result += dx * (k0 * x0 * x0 + v);
742 }
743 break;
744 }
745 /* canonical formula derived with Mathematica */
746 /*case 2:
747 {
748 for (int i = 0; i < num_points__ - 1; i++)
749 {
750 double x0 = f__.x(i);
751 double dx = f__.dx(i);
752
753 auto f = f__.coefs(i);
754 auto g = g__.coefs(i);
755
756 T k0 = f[0] * g[0];
757 T k1 = f[3] * g[1] + f[2] * g[2] + f[1] * g[3];
758 T k2 = f[3] * g[0] + f[2] * g[1] + f[1] * g[2] + f[0] * g[3];
759 T k3 = f[2] * g[0] + f[1] * g[1] + f[0] * g[2];
760 T k4 = f[3] * g[2] + f[2] * g[3];
761 T k5 = f[1] * g[0] + f[0] * g[1];
762 T k6 = f[3] * g[3]; // 25 OPS
763
764 result += dx * (k0 * x0 * x0 +
765 dx * ((x0 * (2.0 * k0 + x0 * k5)) / 2.0 +
766 dx * ((k0 + x0 * (2.0 * k5 + k3 * x0)) / 3.0 +
767 dx * ((k5 + x0 * (2.0 * k3 + k2 * x0)) / 4.0 +
768 dx * ((k3 + x0 * (2.0 * k2 + k1 * x0)) / 5.0 +
769 dx * ((k2 + x0 * (2.0 * k1 + k4 * x0)) / 6.0 +
770 dx * ((k1 + x0 * (2.0 * k4 + k6 * x0)) / 7.0 +
771 dx * ((k4 + 2.0 * k6 * x0) / 8.0 +
772 dx * k6 / 9.0))))))));
773 }
774 break;
775
776 }*/
777
778 default: {
779 throw std::runtime_error("wrong r^m prefactor");
780 }
781 }
782 return result;
783}
784
785template <typename T>
786T inner(Spline<T> const& f__, Spline<T> const& g__, int m__)
787{
788 return inner(f__, g__, m__, f__.num_points());
789}
790
791}; // namespace sirius
792
793#endif // __SPLINE_H__
Base class for radial grids.
Definition: radial_grid.hpp:48
double last() const
Last point of the grid.
sddk::mdarray< double, 1 > x_
Radial grid points.
Definition: radial_grid.hpp:51
double dx(const int i) const
Return .
double first() const
First point of the grid.
void init()
Initialize the grid.
Definition: radial_grid.hpp:64
T x(const int i) const
Return .
int num_points() const
Number of grid points.
Cubic spline with a not-a-knot boundary conditions.
Definition: spline.hpp:76
Spline(Radial_grid< U > const &radial_grid__)
Constructor of a new empty spline.
Definition: spline.hpp:141
Spline(Radial_grid< U > const &radial_grid__, std::vector< T > const &y__)
Constructor of a spline from a list of values.
Definition: spline.hpp:158
T operator()(const int i, U dx) const
Get value at the point x[i] + dx.
Definition: spline.hpp:207
T integrate(std::vector< T > &g__, int m__) const
Integrate spline with r^m prefactor.
Definition: spline.hpp:422
Spline()
Default constructor.
Definition: spline.hpp:137
sddk::mdarray< T, 2 > coeffs_
Array of spline coefficients.
Definition: spline.hpp:79
int solve(T *dl, T *d, T *du, T *b, int n)
Solver tridiagonal system of linear equaitons.
Definition: spline.hpp:85
T & operator()(const int i)
Get the reference to a value at the point x[i].
Definition: spline.hpp:195
T const & operator()(const int i) const
Get value at the point x[i].
Definition: spline.hpp:201
Spline< T, U > & interpolate()
Definition: spline.hpp:275
void init_grid(Radial_grid< U > const &radial_grid__)
Init the underlying radial grid.
Definition: spline.hpp:127
Spline(Radial_grid< U > const &radial_grid__, std::function< T(U)> f__)
Constructor of a spline from a function.
Definition: spline.hpp:148
T at_point(U x) const
Compute value at any point.
Definition: spline.hpp:216
T integrate(int m__) const
Integrate spline with r^m prefactor.
Definition: spline.hpp:579
void zero(memory_t mem__, size_t idx0__, size_t n__)
Zero n elements starting from idx0.
Definition: memory.hpp:1316
void copy(T *target__, T const *source__, size_t n__)
Copy memory inside a device.
Definition: acc.hpp:320
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.
Definition: sirius.f90:5
auto operator*(Spheric_function< function_domain_t::spatial, T > const &a__, Spheric_function< function_domain_t::spatial, T > const &b__)
Multiplication of two functions in spatial domain.
@ du
Down-up block.
Contains declaraion and partial implementation of sirius::Radial_grid class.