Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4PolynomialPDF.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26//
27// -------------------------------------------------------------------
28// GEANT4 Class file
29//
30//
31// File name: G4PolynomialPDF
32//
33// Author: Jason Detwiler ([email protected])
34//
35// Creation date: Aug 2012
36// -------------------------------------------------------------------
37
38#include "G4PolynomialPDF.hh"
39#include "Randomize.hh"
40
41using namespace std;
42
44 G4double x1, G4double x2) :
45 fX1(x1), fX2(x2), fChanged(true), fTolerance(1.e-8), fVerbose(0)
46{
47 if(coeffs != nullptr) SetCoefficients(n, coeffs);
48 else if(n > 0) SetNCoefficients(n);
49}
50
52{}
53
54void G4PolynomialPDF::SetCoefficient(size_t i, G4double value, bool doSimplify)
55{
56 while(i >= fCoefficients.size()) fCoefficients.push_back(0);
57 /* Loop checking, 30-Oct-2015, G.Folger */
58 fCoefficients[i] = value;
59 fChanged = true;
60 if(doSimplify) Simplify();
61}
62
64 const G4double* coefficients)
65{
66 SetNCoefficients(nCoeffs);
67 for(size_t i=0; i<GetNCoefficients(); ++i) {
68 SetCoefficient(i, coefficients[i], false);
69 }
70 fChanged = true;
71 Simplify();
72}
73
75{
76 while(fCoefficients.size() && fCoefficients[fCoefficients.size()-1] == 0) {
77 if(fVerbose > 0) {
78 G4cout << "G4PolynomialPDF::Simplify() WARNING: had to pop coefficient "
79 << fCoefficients.size()-1 << G4endl;
80 }
81 fCoefficients.pop_back();
82 fChanged = true;
83 }
84}
85
87{
88 if(x2 <= x1) {
89 if(fVerbose > 0) {
90 G4cout << "G4PolynomialPDF::SetDomain() WARNING: Invalid domain! "
91 << "(x1 = " << x1 << ", x2 = " << x2 << ")." << G4endl;
92 }
93 return;
94 }
95 fX1 = x1;
96 fX2 = x2;
97 fChanged = true;
98}
99
101{
102 /// Normalize PDF to 1 over domain fX1 to fX2.
103 /// Double-check that the highest-order coefficient is non-zero.
104 while(fCoefficients.size()) { /* Loop checking, 30-Oct-2015, G.Folger */
105 if(fCoefficients[fCoefficients.size()-1] == 0.0) fCoefficients.pop_back();
106 else break;
107 }
108
109 G4double x1N = fX1, x2N = fX2;
110 G4double sum = 0;
111 for(size_t i=0; i<GetNCoefficients(); ++i) {
112 sum += GetCoefficient(i)*(x2N - x1N)/G4double(i+1);
113 x1N*=fX1;
114 x2N*=fX2;
115 }
116 if(sum <= 0) {
117 if(fVerbose > 0) {
118 G4cout << "G4PolynomialPDF::Normalize() WARNING: PDF has non-positive area: "
119 << sum << G4endl;
120 Dump();
121 }
122 return;
123 }
124
125 for(size_t i=0; i<GetNCoefficients(); ++i) {
126 SetCoefficient(i, GetCoefficient(i)/sum, false);
127 }
128 Simplify();
129}
130
132{
133 /// Evaluate f(x)
134 /// ddxPower = -1: f = CDF
135 /// ddxPower = 0: f = PDF
136 /// ddxPower = 1: f = (d/dx) PDF
137 /// ddxPower = 2: f = (d2/dx2) PDF
138 if(ddxPower < -1 || ddxPower > 2) {
139 if(fVerbose > 0) {
140 G4cout << "G4PolynomialPDF::GetX() WARNING: ddxPower " << ddxPower
141 << " not implemented" << G4endl;
142 }
143 return 0.0;
144 }
145
146 double f = 0.; // return value
147 double xN = 1.; // x to the power N
148 double x1N = 1.; // endpoint x1 to the power N; only used by CDF
149 for(size_t i=0; i<=GetNCoefficients(); ++i) {
150 if(ddxPower == -1) { // CDF
151 if(i>0) f += GetCoefficient(i-1)*(xN - x1N)/i;
152 x1N *= fX1;
153 }
154 else if(ddxPower == 0 && i<GetNCoefficients()) f += GetCoefficient(i)*xN; // PDF
155 else if(ddxPower == 1) { // (d/dx) PDF
156 if(i<GetNCoefficients()-1) f += GetCoefficient(i+1)*xN*(i+1);
157 }
158 else if(ddxPower == 2) { // (d2/dx2) PDF
159 if(i<GetNCoefficients()-2) f += GetCoefficient(i+2)*xN*((i+2)*(i+1));
160 }
161 xN *= x;
162 }
163 return f;
164}
165
167{
168 // ax2 + bx + c = 0
169 // p': 2ax + b = 0 -> = 0 at min: x_extreme = -b/2a
170
171 if(x1 < fX1 || x2 > fX2 || x2 < x1) {
172 if(fVerbose > 0) {
173 G4cout << "G4PolynomialPDF::HasNegativeMinimum() WARNING: Invalid range "
174 << x1 << " - " << x2 << G4endl;
175 }
176 return false;
177 }
178
179 // If flat, then check anywhere.
180 if(GetNCoefficients() == 1) return (Evaluate(x1) < -fTolerance);
181
182 // If linear, or if quadratic with negative second derivative,
183 // just check the endpoints
184 if(GetNCoefficients() == 2 ||
185 (GetNCoefficients() == 3 && GetCoefficient(2) <= 0)) {
186 return (Evaluate(x1) < -fTolerance) || (Evaluate(x2) < -fTolerance);
187 }
188
189 // If quadratic and second dervative is positive, check at the mininum
190 if(GetNCoefficients() == 3) {
191 G4double xMin = -GetCoefficient(1)*0.5/GetCoefficient(2);
192 if(xMin < x1) xMin = x1;
193 if(xMin > x2) xMin = x2;
194 return Evaluate(xMin) < -fTolerance;
195 }
196
197 // Higher-order polynomials: consider any extremum between x1 and x2. If none
198 // are found, check the endpoints.
199 G4double extremum = GetX(0, x1, x2, 1);
200 if(Evaluate(extremum) < -fTolerance) return true;
201 else if(extremum <= x1+(x2-x1)*fTolerance ||
202 extremum >= x2-(x2-x1)*fTolerance) return false;
203 else return
204 HasNegativeMinimum(x1, extremum) || HasNegativeMinimum(extremum, x2);
205}
206
208{
209 if(fChanged) {
210 Normalize();
212 if(fVerbose > 0) {
213 G4cout << "G4PolynomialPDF::GetRandomX() WARNING: PDF has negative values, returning 0..."
214 << G4endl;
215 }
216 return 0.0;
217 }
218 fChanged = false;
219 }
221}
222
224 G4int ddxPower, G4double guess, G4bool bisect)
225{
226 /// Find a value of X between x1 and x2 at which f(x) = p.
227 /// ddxPower = -1: f = CDF
228 /// ddxPower = 0: f = PDF
229 /// ddxPower = 1: f = (d/dx) PDF
230 /// Uses the Newton-Raphson method to find the zero of f(x) - p.
231 /// If not found in range, returns the nearest boundary
232
233 // input range checking
234 if(GetNCoefficients() == 0) {
235 if(fVerbose > 0) {
236 G4cout << "G4PolynomialPDF::GetX() WARNING: no PDF defined!" << G4endl;
237 }
238 return x2;
239 }
240 if(ddxPower < -1 || ddxPower > 1) {
241 if(fVerbose > 0) {
242 G4cout << "G4PolynomialPDF::GetX() WARNING: ddxPower " << ddxPower
243 << " not implemented" << G4endl;
244 }
245 return x2;
246 }
247 if(ddxPower == -1 && (p<0 || p>1)) {
248 if(fVerbose > 0) {
249 G4cout << "G4PolynomialPDF::GetX() WARNING: p is out of range" << G4endl;
250 }
251 return fX2;
252 }
253
254 // check limits
255 if(x2 <= x1 || x1 < fX1 || x2 > fX2) {
256 if(fVerbose > 0) {
257 G4cout << "G4PolynomialPDF::GetX() WARNING: domain must have fX1 <= x1 < x2 <= fX2. "
258 << "You sent x1 = " << x1 << ", x2 = " << x2 << "." << G4endl;
259 }
260 return x2;
261 }
262
263 // Return x2 for flat lines
264 if((ddxPower == 0 && GetNCoefficients() == 1) ||
265 (ddxPower == 1 && GetNCoefficients() == 2)) return x2;
266
267 // Solve p = mx + b -> x = (p-b)/m for linear functions
268 if((ddxPower == -1 && GetNCoefficients() == 1) ||
269 (ddxPower == 0 && GetNCoefficients() == 2) ||
270 (ddxPower == 1 && GetNCoefficients() == 3)) {
271 G4double b = (ddxPower > -1) ? GetCoefficient(ddxPower) : -GetCoefficient(0)*fX1;
272 G4double slope = GetCoefficient(ddxPower+1); // the highest-order coefficient
273 if(slope == 0) { // the highest-order coefficient should never be zero if simplified
274 if(fVerbose > 0) {
275 G4cout << "G4PolynomialPDF::GetX() WARNING: Got slope = 0. "
276 << "Did you forget to Simplify()?" << G4endl;
277 }
278 return x2;
279 }
280 if(ddxPower == 1) slope *= 2.;
281 G4double value = (p-b)/slope;
282 if(value < x1) {
283 return x1;
284 }
285 else if(value > x2) {
286 return x2;
287 }
288 else {
289 return value;
290 }
291 }
292
293 // Solve quadratic equation for f-p=0 when f is quadratic
294 if((ddxPower == -1 && GetNCoefficients() == 2) ||
295 (ddxPower == 0 && GetNCoefficients() == 3) ||
296 (ddxPower == 1 && GetNCoefficients() == 4)) {
297 G4double c = -p + ((ddxPower > -1) ? GetCoefficient(ddxPower) : 0);
298 if(ddxPower == -1) c -= (GetCoefficient(0) + GetCoefficient(1)/2.*fX1)*fX1;
299 G4double b = GetCoefficient(ddxPower+1);
300 if(ddxPower == 1) b *= 2.;
301 G4double a = GetCoefficient(ddxPower+2); // the highest-order coefficient
302 if(a == 0) { // the highest-order coefficient should never be 0 if simplified
303 if(fVerbose > 0) {
304 G4cout << "G4PolynomialPDF::GetX() WARNING: Got a = 0. "
305 << "Did you forget to Simplify()?" << G4endl;
306 }
307 return x2;
308 }
309 if(ddxPower == 1) a *= 3;
310 else if(ddxPower == -1) a *= 0.5;
311 double sqrtFactor = b*b - 4.*a*c;
312 if(sqrtFactor < 0) return x2; // quadratic equation has no solution (p not in range of f)
313 sqrtFactor = sqrt(sqrtFactor)/2./fabs(a);
314 G4double valueMinus = -b/2./a - sqrtFactor;
315 if(valueMinus >= x1 && valueMinus <= x2) return valueMinus;
316 else if(valueMinus > x2) return x2;
317 G4double valuePlus = -b/2./a + sqrtFactor;
318 if(valuePlus >= x1 && valuePlus <= x2) return valuePlus;
319 else if(valuePlus < x1) return x2;
320 return (x1-valueMinus <= valuePlus-x2) ? x1 : x2;
321 }
322
323 // f is non-trivial, so use Newton-Raphson
324 // start in the middle if no good guess is provided
325 if(guess < x1 || guess > x2) guess = (x2+x1)*0.5;
326 G4double lastChange = 1;
327 size_t iterations = 0;
328 while(fabs(lastChange) > fTolerance) { /* Loop checking, 02.11.2015, A.Ribon */
329 // calculate f and f' simultaneously
330 G4double f = -p;
331 G4double dfdx = 0;
332 G4double xN = 1;
333 G4double x1N = 1; // only used by CDF
334 for(size_t i=0; i<=GetNCoefficients(); ++i) {
335 if(ddxPower == -1) { // CDF
336 if(i>0) f += GetCoefficient(i-1)*(xN - x1N)/G4double(i);
337 if(i<GetNCoefficients()) dfdx += GetCoefficient(i)*xN;
338 x1N *= fX1;
339 }
340 else if(ddxPower == 0) { // PDF
341 if(i<GetNCoefficients()) f += GetCoefficient(i)*xN;
342 if(i+1<GetNCoefficients()) dfdx += GetCoefficient(i+1)*xN*G4double(i+1);
343 }
344 else { // ddxPower == 1: (d/dx) PDF
345 if(i+1<GetNCoefficients()) f += GetCoefficient(i+1)*xN*G4double(i+1);
346 if(i+2<GetNCoefficients()) dfdx += GetCoefficient(i+2)*xN*G4double(i+2);
347 }
348 xN *= guess;
349 }
350 if(f == 0) return guess;
351 if(dfdx == 0) {
352 if(fVerbose > 0) {
353 G4cout << "G4PolynomialPDF::GetX() WARNING: got f != 0 but slope = 0 for ddxPower = "
354 << ddxPower << G4endl;
355 }
356 return x2;
357 }
358 lastChange = - f/dfdx;
359
360 if(guess + lastChange < x1) {
361 lastChange = x1 - guess;
362 } else if(guess + lastChange > x2) {
363 lastChange = x2 - guess;
364 }
365
366 guess += lastChange;
367 lastChange /= (fX2-fX1);
368
369 ++iterations;
370 if(iterations > 50) {
371 if(p!=0) {
372 if(fVerbose > 0) {
373 G4cout << "G4PolynomialPDF::GetX() WARNING: got stuck searching for " << p
374 << " between " << x1 << " and " << x2 << " with ddxPower = "
375 << ddxPower
376 << ". Last guess was " << guess << "." << G4endl;
377 }
378 }
379 if(ddxPower==-1 && bisect) {
380 if(fVerbose > 0) {
381 G4cout << "G4PolynomialPDF::GetX() WARNING: Biseting and trying again..."
382 << G4endl;
383 }
384 return Bisect(p, x1, x2);
385 }
386 else return guess;
387 }
388 }
389 return guess;
390}
391
393 // Bisect to get 1% precision, then use Newton-Raphson
394 G4double z = (x2 + x1)/2.0; // [x1 z x2]
395 if((x2 - x1)/(fX2 - fX1) < 0.01) return GetX(p, fX1, fX2, -1, z, false);
396 G4double fz = Evaluate(z, -1) - p;
397 if(fz < 0) return Bisect(p, z, x2); // [z x2]
398 return Bisect(p, x1, z); // [x1 z]
399}
400
402{
403 G4cout << "G4PolynomialPDF::Dump() - PDF(x) = ";
404 for(size_t i=0; i<GetNCoefficients(); i++) {
405 if(i>0) G4cout << " + ";
407 if(i>0) G4cout << "*x";
408 if(i>1) G4cout << "^" << i;
409 }
410 G4cout << G4endl;
411 G4cout << "G4PolynomialPDF::Dump() - Interval: " << fX1 << " <= x < "
412 << fX2 << G4endl;
413}
414
415
double G4double
Definition: G4Types.hh:83
bool G4bool
Definition: G4Types.hh:86
int G4int
Definition: G4Types.hh:85
#define G4endl
Definition: G4ios.hh:57
G4GLOB_DLL std::ostream G4cout
#define G4UniformRand()
Definition: Randomize.hh:52
void SetNCoefficients(size_t n)
size_t GetNCoefficients() const
G4double Evaluate(G4double x, G4int ddxPower=0)
void SetCoefficient(size_t i, G4double value, bool doSimplify)
G4double GetCoefficient(size_t i) const
std::vector< G4double > fCoefficients
G4PolynomialPDF(size_t n=0, const double *coeffs=nullptr, G4double x1=0, G4double x2=1)
G4bool HasNegativeMinimum(G4double x1, G4double x2)
G4double GetX(G4double p, G4double x1, G4double x2, G4int ddxPower=0, G4double guess=1.e99, G4bool bisect=true)
void SetDomain(G4double x1, G4double x2)
G4double EvalInverseCDF(G4double p)
void SetCoefficients(const std::vector< G4double > &v)
G4double Bisect(G4double p, G4double x1, G4double x2)
G4double GetRandomX()