@@ -271,19 +271,32 @@ template <typename T, typename Tx, typename Tf>
271271struct setNaturalSplineBoundaryCondition <BoundaryConditionType::Periodic, T, Tx, Tf> {
272272 using Vector = std::vector<T>;
273273 constexpr void operator ()(const Tx& x, const Tf& f, Vector& a, Vector& b, Vector& c, Vector& d) const {
274- const auto nx = x.size ();
275-
276- T dx1 = x[1 ] - x[0 ];
277- T dxN = x[nx-1 ] - x[nx-2 ];
278274
279- b[0 ] = 1.0 ;
280- a[0 ] = -1.0 ; // (lower-left corner)
281- d[0 ] = 0.0 ;
275+ // Periodic boundary conditions, it is assumed that f[0] == f[nx-1] !
282276
283- a[nx-1 ] = 1.0 /dxN;
284- b[nx-1 ] = 2.0 *(1.0 /dxN + 1.0 /dx1);
285- c[nx-1 ] = 1.0 /dx1; // (upper-right corner)
286- d[nx-1 ] = 3.0 *((f[nx-1 ] - f[nx-2 ])/(dxN*dxN) + (f[1 ] - f[0 ])/(dx1*dx1));
277+ const auto nsys = a.size ();
278+
279+ // Row 0
280+ {
281+ T dxL = x.back () - x[nsys-1 ];
282+ T dxR = x[1 ] - x[0 ];
283+
284+ a[0 ] = 1.0 /dxL; // lower-left corner
285+ b[0 ] = 2.0 *(1.0 /dxL + 1.0 /dxR);
286+ c[0 ] = 1.0 /dxR;
287+ d[0 ] = 3.0 *((f[0 ] - f[nsys-1 ])/(dxL*dxL) + (f[1 ] - f[0 ])/(dxR*dxR));
288+ }
289+
290+ // Last row (nsys-1)
291+ {
292+ T dxL = x[nsys-1 ] - x[nsys-2 ];
293+ T dxR = x.back () - x[nsys-1 ];
294+
295+ a[nsys-1 ] = 1.0 /dxL;
296+ b[nsys-1 ] = 2.0 *(1.0 /dxL + 1.0 /dxR);
297+ c[nsys-1 ] = 1.0 /dxR; // upper-right corner
298+ d[nsys-1 ] = 3.0 *((f[nsys-1 ] - f[nsys-2 ])/(dxL*dxL) + (f[0 ] - f[nsys-1 ])/(dxR*dxR));
299+ }
287300
288301 cyclic_thomas_algorithm<T>(a, b, c, d);
289302 }
@@ -297,26 +310,40 @@ std::vector<T> natural_spline_slopes(const Tx x, const Tf f)
297310
298311 const auto nx = x.size ();
299312
313+ // Periodic with duplicated endpoint → reduce system
314+ constexpr bool is_periodic = (BC == BoundaryConditionType::Periodic);
315+ const auto nsys = is_periodic ? nx - 1 : nx;
316+
300317 // vectors that fill up the tridiagonal matrix
301- std::vector<T> a (nx , T{0 }); // first value of a is only used for periodic BC
302- std::vector<T> b (nx , T{0 });
303- std::vector<T> c (nx , T{0 }); // last value of c is only used for periodic BC
318+ std::vector<T> a (nsys , T{0 }); // first value of a is only used for periodic BC
319+ std::vector<T> b (nsys , T{0 });
320+ std::vector<T> c (nsys , T{0 }); // last value of c is only used for periodic BC
304321 // right hand side
305- std::vector<T> d (nx , T{0 });
322+ std::vector<T> d (nsys , T{0 });
306323
307- // rows i = 1, ..., nx-2
308- for (auto i = 1 ; i < nx-1 ; ++i)
324+ for (auto i = 1 ; i < nsys-1 ; ++i)
309325 {
310- T dxi = x[i] - x[i-1 ];
311- T dxi1 = x[i+1 ] - x[i];
312- a[i] = 1.0 /dxi ;
313- b[i] = 2.0 *(1.0 /dxi + 1.0 /dxi1 );
314- c[i] = 1.0 /dxi1 ;
315- d[i] = 3.0 *((f[i] - f[i-1 ])/(dxi*dxi ) + (f[i+1 ] - f[i])/(dxi1*dxi1 ));
326+ T dxL = x[i] - x[i-1 ];
327+ T dxR = x[i+1 ] - x[i];
328+ a[i] = 1.0 /dxL ;
329+ b[i] = 2.0 *(1.0 /dxL + 1.0 /dxR );
330+ c[i] = 1.0 /dxR ;
331+ d[i] = 3.0 *((f[i] - f[i-1 ])/(dxL*dxL ) + (f[i+1 ] - f[i])/(dxR*dxR ));
316332 }
317333
318334 setNaturalSplineBoundaryCondition<BC, T, Tx, Tf>{}(x, f, a, b, c, d);
319335
336+ if constexpr (is_periodic)
337+ {
338+ std::vector<T> full (nx);
339+ for (auto i = 0 ; i < nsys; ++i)
340+ {
341+ full[i] = d[i];
342+ }
343+ full[nx-1 ] = full[0 ]; // duplicate endpoint slope
344+ return full;
345+ }
346+
320347 return d;
321348}
322349
0 commit comments