2// ********************************************************************
3// * License and Disclaimer *
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. *
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. *
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// ********************************************************************
26// G4InterpolationDriver inline implementation
28// Created: D.Sorokin, 2018
29// --------------------------------------------------------------------
31#include "G4FieldUtils.hh"
32#include "G4LineSection.hh"
33#include "G4Exception.hh"
39G4InterpolationDriver<T>::
40G4InterpolationDriver ( G4double hminimum, T* pStepper,
41 G4int numComponents, G4int statisticsVerbose )
42 : G4RKIntegrationDriver<T>(pStepper),
43 fMinimumStep(hminimum),
44 fVerboseLevel(statisticsVerbose)
46 if (numComponents != Base::GetStepper()->GetNumberOfVariables())
48 std::ostringstream message;
49 message << "Driver's number of integrated components "
51 << " != Stepper's number of components "
52 << pStepper->GetNumberOfVariables();
53 G4Exception("G4InterpolationDriver","GeomField0002",
54 FatalException, message);
57 for (G4int i = 0; i < Base::GetMaxNoSteps(); ++i)
60 std::unique_ptr<T>(new T(pStepper->GetSpecificEquation(), // Interpolating stepper must have this!
61 pStepper->GetNumberOfVariables()) ),
62 DBL_MAX, -DBL_MAX, 0.0
66 fLastStepper = fSteppers.end();
70G4InterpolationDriver<T>::~G4InterpolationDriver()
73 if (fVerboseLevel > 0)
75 G4cout << "G4ChordFinder statistics report: \n"
76 << " No trials: " << fTotalNoTrials
77 << " No Calls: " << fNoCalls
78 << " Max-trial: " << fmaxTrials
85void G4InterpolationDriver<T>::OnStartTracking()
87 fChordStepEstimate = DBL_MAX;
89 fTotalStepsForTrack = 0;
93void G4InterpolationDriver<T>::OnComputeStep()
95 fKeepLastStepper = false;
97 fLastStepper = fSteppers.end();
101void G4InterpolationDriver<T>::SetVerboseLevel(G4int level)
103 fVerboseLevel = level;
107G4int G4InterpolationDriver<T>::GetVerboseLevel() const
109 return fVerboseLevel;
113void G4InterpolationDriver<T>::
114Interpolate(G4double curveLength, field_utils::State& y) const
116 if (fLastStepper == fSteppers.end())
118 std::ostringstream message;
119 message << "LOGICK ERROR: fLastStepper == end";
120 G4Exception("G4InterpolationDriver::Interpolate()",
121 "GeomField1001", FatalException, message);
125 ConstStepperIterator end = fLastStepper + 1;
127 auto it = std::lower_bound(fSteppers.cbegin(), end, curveLength,
128 [](const InterpStepper& stepper, G4double value)
130 return stepper.end < value;
135 if (curveLength - fLastStepper->end > CLHEP::perMillion)
137 std::ostringstream message;
138 message << "curveLength = " << curveLength << " > "
139 << fLastStepper->end;
140 G4Exception("G4InterpolationDriver::Interpolate()",
141 "GeomField1001", JustWarning, message);
144 return fLastStepper->stepper->Interpolate(1, y);
147 if (curveLength < it->begin)
149 if (it->begin - curveLength > CLHEP::perMillion)
151 std::ostringstream message;
152 message << "curveLength = " << curveLength << " < " << it->begin;
153 G4Exception("G4InterpolationDriver::Interpolate()",
154 "GeomField1001", JustWarning, message);
157 return it->stepper->Interpolate(0, y);
160 return InterpolateImpl(curveLength, it, y);
164void G4InterpolationDriver<T>::InterpolateImpl(G4double curveLength,
165 ConstStepperIterator it,
166 field_utils::State& y) const
168 const G4double tau = (curveLength - it->begin) * it->inverseLength;
169 return it->stepper->Interpolate(field_utils::clamp(tau, 0., 1.), y);
173G4double G4InterpolationDriver<T>::DistChord(const field_utils::State& yBegin,
174 G4double curveLengthBegin,
175 const field_utils::State& yEnd,
176 G4double curveLengthEnd) const
178 // optimization check if it worth
180 if (curveLengthBegin == fLastStepper->begin &&
181 curveLengthEnd == fLastStepper->end)
183 return fLastStepper->stepper->DistChord();
186 const G4double curveLengthMid = 0.5 * (curveLengthBegin + curveLengthEnd);
187 field_utils::State yMid;
189 Interpolate(curveLengthMid, yMid);
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)
199G4double G4InterpolationDriver<T>::AdvanceChordLimited(G4FieldTrack& track,
202 G4double chordDistance)
204 ++fTotalStepsForTrack;
206 const G4double curveLengthBegin = track.GetCurveLength();
207 const G4double hend = std::min(hstep, fChordStepEstimate);
209 auto it = fSteppers.begin();
210 G4double dChordStep = 0.0;
212 field_utils::State yBegin, y;
213 track.DumpToArray(yBegin);
214 track.DumpToArray(y);
218 Base::GetEquationOfMotion()->RightHandSide(y, fdydx);
222 if (fKeepLastStepper)
224 std::swap(*fSteppers.begin(), *fLastStepper);
225 it = fSteppers.begin(); //new begin, update iterator
227 hdid = it->end - curveLengthBegin;
231 InterpolateImpl(curveLengthBegin + hdid, it, y);
235 field_utils::copy(y, it->stepper->GetYOut());
238 dChordStep = DistChord(yBegin, curveLengthBegin, y,
239 curveLengthBegin + hdid);
244 // accurate advance & check chord distance
246 for (; hdid<hend && dChordStep<chordDistance && it!=fSteppers.end(); ++it)
248 h = std::min(h, hstep - hdid);
251 hdid += OneGoodStep(it, y, fdydx, h, epsStep, curveLengthBegin + hdid);
253 // update last stepper
256 // estimate chord distance
257 dChordStep = std::max( dChordStep,
258 DistChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid) );
262 // - full integration ( hdid >= hend )
263 // - estimated chord has exceeded limit 'chordDistance'
264 // - reached maximum number of steps (from number of steppers.)
266 // update step estimation
267 if (h > fMinimumStep)
274 // update chord step estimate
276 hdid = FindNextChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid,
277 dChordStep, chordDistance);
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);
288G4double G4InterpolationDriver<T>::
289FindNextChord(const field_utils::State& yBegin,
290 G4double curveLengthBegin,
291 field_utils::State& yEnd,
292 G4double curveLengthEnd,
294 G4double chordDistance)
296 G4double hstep = curveLengthEnd - curveLengthBegin;
297 G4double curveLength = curveLengthEnd;
300 for (; i < fMaxTrials && dChord > chordDistance
301 && curveLength > fLastStepper->begin; ++i)
304 hstep = CalcChordStep(hstep, dChord, chordDistance);
306 // hstep should be in the last stepper
307 hstep = std::max(hstep, fLastStepper->begin - curveLengthBegin);
308 curveLength = curveLengthBegin + hstep;
311 InterpolateImpl(curveLength, fLastStepper, yEnd);
313 // update chord distance
314 dChord = DistChord(yBegin, curveLengthBegin, yEnd, curveLength);
317 // dChord may be zero
321 fChordStepEstimate = hstep * std::sqrt(chordDistance / dChord);
326 G4Exception("G4InterpolationDriver::FindNextChord()",
327 "GeomField1001", JustWarning, "cannot converge");
330 AccumulateStatistics(i);
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'.
340G4double G4InterpolationDriver<T>::CalcChordStep(G4double stepTrialOld,
342 G4double chordDistance)
344 const G4double chordStepEstimate = stepTrialOld
345 * std::sqrt(chordDistance / dChordStep);
346 G4double stepTrial = fFractionNextEstimate * chordStepEstimate;
348 if (stepTrial <= 0.001 * stepTrialOld)
350 if (dChordStep > 1000.0 * chordDistance)
352 stepTrial = stepTrialOld * 0.03;
356 if (dChordStep > 100. * chordDistance)
358 stepTrial = stepTrialOld * 0.1;
360 else // Try halving the length until dChordStep OK
362 stepTrial = stepTrialOld * 0.5;
366 else if (stepTrial > 1000.0 * stepTrialOld)
368 stepTrial = 1000.0 * stepTrialOld;
371 if (stepTrial == 0.0)
373 stepTrial = 0.000001;
376 // A more sophisticated chord-finder could figure out a better
377 // stepTrial, from dChordStep and the required d_geometry
379 // Calculate R, r_helix (eg at orig point)
380 // if( stepTrial < 2 pi R )
381 // stepTrial = R arc_cos( 1 - chordDistance / r_helix )
389G4bool G4InterpolationDriver<T>::
390AccurateAdvance(G4FieldTrack& track, G4double hstep,
392 G4double /*hinitial*/
397 std::ostringstream message;
398 message << "Proposed step is zero; hstep = " << hstep << " !";
399 G4Exception("G4InterpolationDriver::AccurateAdvance()",
400 "GeomField1001", JustWarning, message);
406 std::ostringstream message;
407 message << "Invalid run condition." << G4endl
408 << "Proposed step is negative; hstep = " << hstep << "."
410 << "Requested step cannot be negative! Aborting event.";
411 G4Exception("G4InterpolationDriver::AccurateAdvance()",
412 "GeomField0003", EventMustBeAborted, message);
416 const G4double curveLength = track.GetCurveLength();
417 const G4double curveLengthEnd = curveLength + hstep;
419 field_utils::State y;
420 Interpolate(curveLengthEnd, y);
422 track.LoadFromArray(y, Base::GetStepper()->GetNumberOfVariables());
423 track.SetCurveLength(curveLengthEnd);
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
442G4double G4InterpolationDriver<T>::OneGoodStep(StepperIterator it,
443 field_utils::State& y,
444 field_utils::State& dydx,
447 G4double curveLength)
450 G4double error2 = DBL_MAX;
451 field_utils::State yerr, ytemp, dydxtemp;
455 for (; i < fMaxTrials; ++i)
457 it->stepper->Stepper(y, dydx, h, ytemp, yerr, dydxtemp);
458 error2 = field_utils::relativeError2(y, yerr, h, epsStep);
462 hstep = std::max(Base::GrowStepSize2(h, error2), fMinimumStep);
466 // don't control error for small steps
467 if (h <= fMinimumStep)
469 hstep = fMinimumStep;
473 h = std::max(Base::ShrinkStepSize2(h, error2), fMinimumStep);
478 G4Exception("G4InterpolationDriver::OneGoodStep()",
479 "GeomField1001", JustWarning, "cannot converge");
480 hstep = std::max(Base::ShrinkStepSize2(h, error2), fMinimumStep);
483 // set interpolation inverval
484 it->begin = curveLength;
485 it->end = curveLength + h;
486 it->inverseLength = 1. / h;
488 // setup interpolation
489 it->stepper->SetupInterpolation();
491 field_utils::copy(dydx, dydxtemp);
492 field_utils::copy(y, ytemp);
498void G4InterpolationDriver<T>::PrintState() const
500 using namespace field_utils;
501 State prevEnd, currBegin;
502 auto prev = fSteppers.begin();
504 G4cout << "====== curr state ========" << G4endl;
505 for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
507 i->stepper->Interpolate(0, currBegin);
509 G4cout << "cl_begin: " <<i->begin << " "
510 << "cl_end: " << i->end << " ";
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();
523 const G4double clBegin = fSteppers.begin()->begin;
524 const G4double clEnd = fLastStepper->end;
525 const G4double hstep = (clEnd - clBegin) / 10.;
527 Interpolate(0, yBegin);
528 for (G4double cl = clBegin; cl <= clEnd + 1e-12; cl += hstep)
530 Interpolate(cl, yCurr);
531 auto d = DistChord(yBegin, clBegin, yCurr, cl);
532 G4cout << "cl: " << cl << " chord_distance: " << d << G4endl;
535 G4cout << "==========================" << G4endl;
540void G4InterpolationDriver<T>::CheckState() const
542 G4int smallSteps = 0;
543 for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
545 G4double stepLength = i->end - i->begin;
546 if (stepLength < fMinimumStep)
554 std::ostringstream message;
555 message << "====== curr state ========\n";
556 for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
558 message << "cl_begin: " <<i->begin << " "
559 << "cl_end: " << i->end << "\n";
562 G4Exception("G4InterpolationDriver::CheckState()",
563 "GeomField0003", FatalException, message);
568void G4InterpolationDriver<T>::AccumulateStatistics(G4int noTrials)
570 fTotalNoTrials += noTrials;
573 if (noTrials > fmaxTrials)
575 fmaxTrials = noTrials;
580void G4InterpolationDriver<T>::StreamInfo( std::ostream& os ) const
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;
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;
597 os << " VerboseLevel = " << fVerboseLevel << std::endl;
598 os << " KeepLastStepper = " << fKeepLastStepper << std::endl;