Geant4-11
G4InterpolationDriver.icc
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// G4InterpolationDriver inline implementation
27//
28// Created: D.Sorokin, 2018
29// --------------------------------------------------------------------
30
31#include "G4FieldUtils.hh"
32#include "G4LineSection.hh"
33#include "G4Exception.hh"
34
35#include <algorithm>
36
37
38template <class T>
39G4InterpolationDriver<T>::
40G4InterpolationDriver ( G4double hminimum, T* pStepper,
41 G4int numComponents, G4int statisticsVerbose )
42 : G4RKIntegrationDriver<T>(pStepper),
43 fMinimumStep(hminimum),
44 fVerboseLevel(statisticsVerbose)
45{
46 if (numComponents != Base::GetStepper()->GetNumberOfVariables())
47 {
48 std::ostringstream message;
49 message << "Driver's number of integrated components "
50 << numComponents
51 << " != Stepper's number of components "
52 << pStepper->GetNumberOfVariables();
53 G4Exception("G4InterpolationDriver","GeomField0002",
54 FatalException, message);
55 }
56
57 for (G4int i = 0; i < Base::GetMaxNoSteps(); ++i)
58 {
59 fSteppers.push_back({
60 std::unique_ptr<T>(new T(pStepper->GetSpecificEquation(), // Interpolating stepper must have this!
61 pStepper->GetNumberOfVariables()) ),
62 DBL_MAX, -DBL_MAX, 0.0
63 });
64 }
65
66 fLastStepper = fSteppers.end();
67}
68
69template <class T>
70G4InterpolationDriver<T>::~G4InterpolationDriver()
71{
72#ifdef G4VERBOSE
73 if (fVerboseLevel > 0)
74 {
75 G4cout << "G4ChordFinder statistics report: \n"
76 << " No trials: " << fTotalNoTrials
77 << " No Calls: " << fNoCalls
78 << " Max-trial: " << fmaxTrials
79 << G4endl;
80 }
81#endif
82}
83
84template <class T>
85void G4InterpolationDriver<T>::OnStartTracking()
86{
87 fChordStepEstimate = DBL_MAX;
88 fhnext = DBL_MAX;
89 fTotalStepsForTrack = 0;
90}
91
92template <class T>
93void G4InterpolationDriver<T>::OnComputeStep()
94{
95 fKeepLastStepper = false;
96 fFirstStep = true;
97 fLastStepper = fSteppers.end();
98}
99
100template <class T>
101void G4InterpolationDriver<T>::SetVerboseLevel(G4int level)
102{
103 fVerboseLevel = level;
104}
105
106template <class T>
107G4int G4InterpolationDriver<T>::GetVerboseLevel() const
108{
109 return fVerboseLevel;
110}
111
112template <class T>
113void G4InterpolationDriver<T>::
114Interpolate(G4double curveLength, field_utils::State& y) const
115{
116 if (fLastStepper == fSteppers.end())
117 {
118 std::ostringstream message;
119 message << "LOGICK ERROR: fLastStepper == end";
120 G4Exception("G4InterpolationDriver::Interpolate()",
121 "GeomField1001", FatalException, message);
122 return;
123 }
124
125 ConstStepperIterator end = fLastStepper + 1;
126
127 auto it = std::lower_bound(fSteppers.cbegin(), end, curveLength,
128 [](const InterpStepper& stepper, G4double value)
129 {
130 return stepper.end < value;
131 }
132 );
133 if (it == end)
134 {
135 if (curveLength - fLastStepper->end > CLHEP::perMillion)
136 {
137 std::ostringstream message;
138 message << "curveLength = " << curveLength << " > "
139 << fLastStepper->end;
140 G4Exception("G4InterpolationDriver::Interpolate()",
141 "GeomField1001", JustWarning, message);
142 }
143
144 return fLastStepper->stepper->Interpolate(1, y);
145 }
146
147 if (curveLength < it->begin)
148 {
149 if (it->begin - curveLength > CLHEP::perMillion)
150 {
151 std::ostringstream message;
152 message << "curveLength = " << curveLength << " < " << it->begin;
153 G4Exception("G4InterpolationDriver::Interpolate()",
154 "GeomField1001", JustWarning, message);
155 }
156
157 return it->stepper->Interpolate(0, y);
158 }
159
160 return InterpolateImpl(curveLength, it, y);
161}
162
163template <class T>
164void G4InterpolationDriver<T>::InterpolateImpl(G4double curveLength,
165 ConstStepperIterator it,
166 field_utils::State& y) const
167{
168 const G4double tau = (curveLength - it->begin) * it->inverseLength;
169 return it->stepper->Interpolate(field_utils::clamp(tau, 0., 1.), y);
170}
171
172template <class T>
173G4double G4InterpolationDriver<T>::DistChord(const field_utils::State& yBegin,
174 G4double curveLengthBegin,
175 const field_utils::State& yEnd,
176 G4double curveLengthEnd) const
177{
178 // optimization check if it worth
179 //
180 if (curveLengthBegin == fLastStepper->begin &&
181 curveLengthEnd == fLastStepper->end)
182 {
183 return fLastStepper->stepper->DistChord();
184 }
185
186 const G4double curveLengthMid = 0.5 * (curveLengthBegin + curveLengthEnd);
187 field_utils::State yMid;
188
189 Interpolate(curveLengthMid, yMid);
190
191 return G4LineSection::Distline(
192 field_utils::makeVector(yMid, field_utils::Value3D::Position),
193 field_utils::makeVector(yBegin, field_utils::Value3D::Position),
194 field_utils::makeVector(yEnd, field_utils::Value3D::Position)
195 );
196}
197
198template <class T>
199G4double G4InterpolationDriver<T>::AdvanceChordLimited(G4FieldTrack& track,
200 G4double hstep,
201 G4double epsStep,
202 G4double chordDistance)
203{
204 ++fTotalStepsForTrack;
205
206 const G4double curveLengthBegin = track.GetCurveLength();
207 const G4double hend = std::min(hstep, fChordStepEstimate);
208 G4double hdid = 0.0;
209 auto it = fSteppers.begin();
210 G4double dChordStep = 0.0;
211
212 field_utils::State yBegin, y;
213 track.DumpToArray(yBegin);
214 track.DumpToArray(y);
215
216 if (fFirstStep)
217 {
218 Base::GetEquationOfMotion()->RightHandSide(y, fdydx);
219 fFirstStep = false;
220 }
221
222 if (fKeepLastStepper)
223 {
224 std::swap(*fSteppers.begin(), *fLastStepper);
225 it = fSteppers.begin(); //new begin, update iterator
226 fLastStepper = it;
227 hdid = it->end - curveLengthBegin;
228 if (hdid > hend)
229 {
230 hdid = hend;
231 InterpolateImpl(curveLengthBegin + hdid, it, y);
232 }
233 else
234 {
235 field_utils::copy(y, it->stepper->GetYOut());
236 }
237
238 dChordStep = DistChord(yBegin, curveLengthBegin, y,
239 curveLengthBegin + hdid);
240
241 ++it;
242 }
243
244 // accurate advance & check chord distance
245 G4double h = fhnext;
246 for (; hdid<hend && dChordStep<chordDistance && it!=fSteppers.end(); ++it)
247 {
248 h = std::min(h, hstep - hdid);
249
250 // make one step
251 hdid += OneGoodStep(it, y, fdydx, h, epsStep, curveLengthBegin + hdid);
252
253 // update last stepper
254 fLastStepper = it;
255
256 // estimate chord distance
257 dChordStep = std::max( dChordStep,
258 DistChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid) );
259 }
260
261 // Now, either
262 // - full integration ( hdid >= hend )
263 // - estimated chord has exceeded limit 'chordDistance'
264 // - reached maximum number of steps (from number of steppers.)
265
266 // update step estimation
267 if (h > fMinimumStep)
268 {
269 fhnext = h;
270 }
271
272 // CheckState();
273
274 // update chord step estimate
275 //
276 hdid = FindNextChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid,
277 dChordStep, chordDistance);
278
279 const G4double curveLengthEnd = curveLengthBegin + hdid;
280 fKeepLastStepper = fLastStepper->end - curveLengthEnd > CLHEP::perMillion;
281 track.LoadFromArray(y, fLastStepper->stepper->GetNumberOfVariables());
282 track.SetCurveLength(curveLengthBegin + hdid);
283
284 return hdid;
285}
286
287template <class T>
288G4double G4InterpolationDriver<T>::
289FindNextChord(const field_utils::State& yBegin,
290 G4double curveLengthBegin,
291 field_utils::State& yEnd,
292 G4double curveLengthEnd,
293 G4double dChord,
294 G4double chordDistance)
295{
296 G4double hstep = curveLengthEnd - curveLengthBegin;
297 G4double curveLength = curveLengthEnd;
298
299 G4int i = 1;
300 for (; i < fMaxTrials && dChord > chordDistance
301 && curveLength > fLastStepper->begin; ++i)
302 {
303 // crop step size
304 hstep = CalcChordStep(hstep, dChord, chordDistance);
305
306 // hstep should be in the last stepper
307 hstep = std::max(hstep, fLastStepper->begin - curveLengthBegin);
308 curveLength = curveLengthBegin + hstep;
309
310 // use fLastStepper!
311 InterpolateImpl(curveLength, fLastStepper, yEnd);
312
313 // update chord distance
314 dChord = DistChord(yBegin, curveLengthBegin, yEnd, curveLength);
315 }
316
317 // dChord may be zero
318 //
319 if (dChord > 0.0)
320 {
321 fChordStepEstimate = hstep * std::sqrt(chordDistance / dChord);
322 }
323
324 if (i == fMaxTrials)
325 {
326 G4Exception("G4InterpolationDriver::FindNextChord()",
327 "GeomField1001", JustWarning, "cannot converge");
328 }
329
330 AccumulateStatistics(i);
331
332 return hstep;
333}
334
335// Is called to estimate the next step size, even for successful steps,
336// in order to predict an accurate 'chord-sensitive' first step
337// which is likely to assist in more performant 'stepping'.
338//
339template <class T>
340G4double G4InterpolationDriver<T>::CalcChordStep(G4double stepTrialOld,
341 G4double dChordStep,
342 G4double chordDistance)
343{
344 const G4double chordStepEstimate = stepTrialOld
345 * std::sqrt(chordDistance / dChordStep);
346 G4double stepTrial = fFractionNextEstimate * chordStepEstimate;
347
348 if (stepTrial <= 0.001 * stepTrialOld)
349 {
350 if (dChordStep > 1000.0 * chordDistance)
351 {
352 stepTrial = stepTrialOld * 0.03;
353 }
354 else
355 {
356 if (dChordStep > 100. * chordDistance)
357 {
358 stepTrial = stepTrialOld * 0.1;
359 }
360 else // Try halving the length until dChordStep OK
361 {
362 stepTrial = stepTrialOld * 0.5;
363 }
364 }
365 }
366 else if (stepTrial > 1000.0 * stepTrialOld)
367 {
368 stepTrial = 1000.0 * stepTrialOld;
369 }
370
371 if (stepTrial == 0.0)
372 {
373 stepTrial = 0.000001;
374 }
375
376 // A more sophisticated chord-finder could figure out a better
377 // stepTrial, from dChordStep and the required d_geometry
378 // e.g.
379 // Calculate R, r_helix (eg at orig point)
380 // if( stepTrial < 2 pi R )
381 // stepTrial = R arc_cos( 1 - chordDistance / r_helix )
382 // else
383 // ??
384
385 return stepTrial;
386}
387
388template <class T>
389G4bool G4InterpolationDriver<T>::
390AccurateAdvance(G4FieldTrack& track, G4double hstep,
391 G4double /*eps*/,
392 G4double /*hinitial*/
393 )
394{
395 if (hstep == 0.0)
396 {
397 std::ostringstream message;
398 message << "Proposed step is zero; hstep = " << hstep << " !";
399 G4Exception("G4InterpolationDriver::AccurateAdvance()",
400 "GeomField1001", JustWarning, message);
401 return true;
402 }
403
404 if (hstep < 0)
405 {
406 std::ostringstream message;
407 message << "Invalid run condition." << G4endl
408 << "Proposed step is negative; hstep = " << hstep << "."
409 << G4endl
410 << "Requested step cannot be negative! Aborting event.";
411 G4Exception("G4InterpolationDriver::AccurateAdvance()",
412 "GeomField0003", EventMustBeAborted, message);
413 return false;
414 }
415
416 const G4double curveLength = track.GetCurveLength();
417 const G4double curveLengthEnd = curveLength + hstep;
418
419 field_utils::State y;
420 Interpolate(curveLengthEnd, y);
421
422 track.LoadFromArray(y, Base::GetStepper()->GetNumberOfVariables());
423 track.SetCurveLength(curveLengthEnd);
424
425 return true;
426}
427
428// Driver for one Runge-Kutta Step with monitoring of local truncation error
429// to ensure accuracy and adjust stepsize. Input are dependent variable
430// array y[0,...,5] and its derivative dydx[0,...,5] at the
431// starting value of the independent variable x . Also input are stepsize
432// to be attempted htry, and the required accuracy eps. On output y and x
433// are replaced by their new values, hdid is the stepsize that was actually
434// accomplished, and hnext is the estimated next stepsize.
435// This is similar to the function rkqs from the book:
436// Numerical Recipes in C: The Art of Scientific Computing (NRC), Second
437// Edition, by William H. Press, Saul A. Teukolsky, William T.
438// Vetterling, and Brian P. Flannery (Cambridge University Press 1992),
439// 16.2 Adaptive StepSize Control for Runge-Kutta, p. 719
440//
441template <class T>
442G4double G4InterpolationDriver<T>::OneGoodStep(StepperIterator it,
443 field_utils::State& y,
444 field_utils::State& dydx,
445 G4double& hstep,
446 G4double epsStep,
447 G4double curveLength)
448
449{
450 G4double error2 = DBL_MAX;
451 field_utils::State yerr, ytemp, dydxtemp;
452 G4double h = hstep;
453
454 G4int i = 0;
455 for (; i < fMaxTrials; ++i)
456 {
457 it->stepper->Stepper(y, dydx, h, ytemp, yerr, dydxtemp);
458 error2 = field_utils::relativeError2(y, yerr, h, epsStep);
459
460 if (error2 <= 1.0)
461 {
462 hstep = std::max(Base::GrowStepSize2(h, error2), fMinimumStep);
463 break;
464 }
465
466 // don't control error for small steps
467 if (h <= fMinimumStep)
468 {
469 hstep = fMinimumStep;
470 break;
471 }
472
473 h = std::max(Base::ShrinkStepSize2(h, error2), fMinimumStep);
474 }
475
476 if (i == fMaxTrials)
477 {
478 G4Exception("G4InterpolationDriver::OneGoodStep()",
479 "GeomField1001", JustWarning, "cannot converge");
480 hstep = std::max(Base::ShrinkStepSize2(h, error2), fMinimumStep);
481 }
482
483 // set interpolation inverval
484 it->begin = curveLength;
485 it->end = curveLength + h;
486 it->inverseLength = 1. / h;
487
488 // setup interpolation
489 it->stepper->SetupInterpolation();
490
491 field_utils::copy(dydx, dydxtemp);
492 field_utils::copy(y, ytemp);
493
494 return h;
495}
496
497template <class T>
498void G4InterpolationDriver<T>::PrintState() const
499{
500 using namespace field_utils;
501 State prevEnd, currBegin;
502 auto prev = fSteppers.begin();
503
504 G4cout << "====== curr state ========" << G4endl;
505 for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
506 {
507 i->stepper->Interpolate(0, currBegin);
508
509 G4cout << "cl_begin: " <<i->begin << " "
510 << "cl_end: " << i->end << " ";
511
512 if (prev != i) {
513 prev->stepper->Interpolate(1, prevEnd);
514 auto prevPos = makeVector(prevEnd, Value3D::Position);
515 auto currPos = makeVector(currBegin, Value3D::Position);
516 G4cout << "diff_begin: " << (prevPos - currPos).mag();
517 }
518
519 G4cout << G4endl;
520 prev = i;
521 }
522
523 const G4double clBegin = fSteppers.begin()->begin;
524 const G4double clEnd = fLastStepper->end;
525 const G4double hstep = (clEnd - clBegin) / 10.;
526 State yBegin, yCurr;
527 Interpolate(0, yBegin);
528 for (G4double cl = clBegin; cl <= clEnd + 1e-12; cl += hstep)
529 {
530 Interpolate(cl, yCurr);
531 auto d = DistChord(yBegin, clBegin, yCurr, cl);
532 G4cout << "cl: " << cl << " chord_distance: " << d << G4endl;
533 }
534
535 G4cout << "==========================" << G4endl;
536}
537
538
539template <class T>
540void G4InterpolationDriver<T>::CheckState() const
541{
542 G4int smallSteps = 0;
543 for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
544 {
545 G4double stepLength = i->end - i->begin;
546 if (stepLength < fMinimumStep)
547 {
548 ++smallSteps;
549 }
550 }
551
552 if (smallSteps > 1)
553 {
554 std::ostringstream message;
555 message << "====== curr state ========\n";
556 for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
557 {
558 message << "cl_begin: " <<i->begin << " "
559 << "cl_end: " << i->end << "\n";
560 }
561
562 G4Exception("G4InterpolationDriver::CheckState()",
563 "GeomField0003", FatalException, message);
564 }
565}
566
567template <class T>
568void G4InterpolationDriver<T>::AccumulateStatistics(G4int noTrials)
569{
570 fTotalNoTrials += noTrials;
571 ++fNoCalls;
572
573 if (noTrials > fmaxTrials)
574 {
575 fmaxTrials = noTrials;
576 }
577}
578
579template <class T>
580void G4InterpolationDriver<T>::StreamInfo( std::ostream& os ) const
581{
582 os << "State of G4InterpolationDriver: " << std::endl;
583 os << "--Base state (G4RKIntegrationDriver): " << std::endl;
584 Base::StreamInfo( os );
585 os << " fMinimumStep = " << fMinimumStep << std::endl;
586 // os << " Max number of Steps = " << fMaxNoSteps << std::endl;
587 // os << " Safety factor = " << safety << std::endl;
588 // os << " Power - shrink = " << pshrnk << std::endl;
589 // os << " Power - grow = " << pgrow << std::endl;
590 // os << " threshold - shrink = " << errorConstraintShrink << std::endl;
591 // os << " threshold - grow = " << errorConstraintGrow << std::endl;
592
593 os << " Max num of Trials = " << fMaxTrials << std::endl;
594 os << " Fract Next Estimate = " << fFractionNextEstimate << std::endl;
595 os << " Smallest Curve Fract= " << fSmallestCurveFraction << std::endl;
596
597 os << " VerboseLevel = " << fVerboseLevel << std::endl;
598 os << " KeepLastStepper = " << fKeepLastStepper << std::endl;
599}