MARLEY (Model of Argon Reaction Low Energy Yields) v2.0.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
Loading...
Searching...
No Matches
ChebyshevInterpolatingFunction.cc
1
4//
5// This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6//
7// MARLEY is free software: you can redistribute it and/or modify it under the
8// terms of version 3 of the GNU General Public License as published by the
9// Free Software Foundation.
10//
11// For the full text of the license please see COPYING or
12// visit http://opensource.org/licenses/GPL-3.0
13//
14// Please respect the MCnet academic usage guidelines. See GUIDELINES
15// or visit https://www.montecarlonet.org/GUIDELINES for details.
16
17// Standard library includes
18#include <algorithm>
19#include <cmath>
20#include <iostream>
21#include <limits>
22
23// FFTPACK4 includes
24#include "builtin/fftpack4/fftpack4.h"
25#include "builtin/fftpack4/fftpack4_precision.h"
26
27// MARLEY includes
28#include "marley/ChebyshevInterpolatingFunction.hh"
29
30namespace {
31 constexpr double MY_EPSILON = std::numeric_limits<double>::epsilon();
32}
33
35 const std::function<double(double)>& func, double x_min, double x_max,
36 size_t N )
37 : marley::InterpolatingFunction( x_min, x_max )
38{
39 bool ok;
40
41 if ( N != 0 ) {
42 ok = true;
43 N_ = N;
44 }
45 else {
46 ok = false;
47 N_ = 1;
48 }
49
50 // Adaptively find a good grid size
51 // @todo add more explanation
52 do {
53
54 if ( !ok ) N_ *= 2;
55
56 //std::cout << "N = " << N_ << '\n';
57
58 // @todo optimize this
59 Xs_.clear();
60 Fs_.clear();
61
62 for ( size_t j = 0; j <= N_; ++j ) {
63 double x = chebyshev_point( j );
64 double f = func(x);
65 Xs_.push_back( x );
66 Fs_.push_back( f );
67 }
68
70 wsave_ = std::vector<double>(3*Fs_.size() + 15, 0.);
71 ifac_ = std::vector<int>(Fs_.size() / 2, 0);
72
73 int my_size = Fs_.size();
74 costi( &my_size, wsave_.data(), ifac_.data() );
75 cost( &my_size, chebyshev_coeffs_.data(), wsave_.data(), ifac_.data() );
76
77 double biggest_coeff = *std::max_element(chebyshev_coeffs_.cbegin(),
78 chebyshev_coeffs_.cend(), [](double left, double right) -> double
79 { return std::abs(left) < std::abs(right); });
80
81 double upper_limit = 2. * std::abs( biggest_coeff ) * MY_EPSILON;
82
83 double last_coeff_mag = std::abs( chebyshev_coeffs_.back() );
84 double next_to_last_coeff_mag = std::abs(
85 chebyshev_coeffs_.at( chebyshev_coeffs_.size() - 2 ));
86 if ( last_coeff_mag < upper_limit && next_to_last_coeff_mag < upper_limit )
87 {
88 ok = true;
89 }
90
91 if ( N_ >= N_MAX_ ) {
93 ok = true;
94 }
95
96 } while ( !ok );
97
98 // Normalize the Chebyshev coefficients by dividing by N
99 for (double& d : chebyshev_coeffs_) d /= N_;
100
101 compute_integral();
102}
103
104void marley::ChebyshevInterpolatingFunction::compute_integral() {
105 // Compute the integral over [x_min_, x_max_] via Clenshaw-Curtis
106 // quadrature
107 integral_ = 0.;
108 for (size_t k = 0; k <= N_; k += 2) {
109 // Only even Chebyshev polynomials contribute to this integral
110 double term = chebyshev_coeffs_.at( k ) * 2.0 / ( 1. - std::pow(k, 2) );
111 if ( k == 0 || k == N_ ) term /= 2.;
112 integral_ += term;
113 }
114
115 // Extra factor to account for integration over [x_min_, x_max_] instead
116 // of the standard interval [-1, 1] for the Chebyshev polynomials
117 integral_ *= (x_max_ - x_min_) / 2.;
118}
119
120
121marley::ChebyshevInterpolatingFunction
122 marley::ChebyshevInterpolatingFunction::cdf() const
123{
124 marley::ChebyshevInterpolatingFunction result;
125 result.N_ = this->N_ + 1;
126 result.x_min_ = this->x_min_;
127 result.x_max_ = this->x_max_;
128
129 double beta_0 = this->chebyshev_coeffs_.at(0);
130 beta_0 += -0.5 * this->chebyshev_coeffs_.at(1);
131 for ( size_t j = 2; j <= this->N_; ++j ) {
132 beta_0 += 2.*this->chebyshev_coeffs_.at(j)
133 * std::pow(-1.0, j + 1) / (j*j - 1.0);
134 }
135
136 result.chebyshev_coeffs_.push_back( beta_0 );
137 for ( size_t k = 1; k < this->N_; ++k ) {
138 double ak_minus_one = this->chebyshev_coeffs_.at( k - 1 );
139 double ak_plus_one = this->chebyshev_coeffs_.at( k + 1 );
140 result.chebyshev_coeffs_.push_back(
141 (ak_minus_one - ak_plus_one) / (2 * k) );
142 }
143 result.chebyshev_coeffs_.push_back( this->chebyshev_coeffs_.at( N_ - 1 )
144 / (2 * N_) );
145 result.chebyshev_coeffs_.push_back( this->chebyshev_coeffs_.at( N_ )
146 / (2 * (N_ + 1)) );
147
148 for (double& d : result.chebyshev_coeffs_) d *= (x_max_ - x_min_) / 2.;
149
150 result.compute_integral();
151
152 for ( size_t j = 0; j <= result.N_; ++j ) {
153 double x = result.chebyshev_point( j );
154 result.Xs_.push_back( x );
155 }
156
157 result.wsave_ = std::vector<double>(
158 3*result.chebyshev_coeffs_.size() + 15, 0.);
159 result.ifac_ = std::vector<int>(result.chebyshev_coeffs_.size() / 2, 0);
160
161 int my_size = result.chebyshev_coeffs_.size();
162 result.Fs_ = result.chebyshev_coeffs_;
163 costi( &my_size, result.wsave_.data(), result.ifac_.data() );
164 cost( &my_size, result.Fs_.data(), result.wsave_.data(), result.ifac_.data() );
165 for (double& d : result.Fs_) d *= 0.5;
166
167 return result;
168}
static constexpr size_t N_MAX_
Maximum allowed value of the grid size parameter.
size_t N_
Grid size parameter (N + 1 total points)
ChebyshevInterpolatingFunction()
Default constructor used by cdf()
double chebyshev_point(size_t j) const
For a given N, returns the x position of the jth Chebyshev point (of the second kind)
std::vector< double > Xs_
Chebyshev points at which the function was evaluated.
std::vector< double > chebyshev_coeffs_
Coefficients of the Chebyshev expansion of this function.
std::vector< double > Fs_
Function values at the grid points.
double x_max_
Upper bound of the interpolation range.
double x_min_
Lower bound of the interpolation range.