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// G4IntegrationDriver inline implementation
28// Author: Dmitry Sorokin, Google Summer of Code 2017
29// Supervision: John Apostolakis, CERN
30// --------------------------------------------------------------------
32#include "G4FieldUtils.hh"
35G4IntegrationDriver<T>::
36G4IntegrationDriver ( G4double hminimum, T* pStepper,
37 G4int numComponents, G4int statisticsVerbose )
38 : G4RKIntegrationDriver<T>(pStepper),
39 fMinimumStep(hminimum),
40 fSmallestFraction(1e-12),
41 fVerboseLevel(statisticsVerbose),
42 fNoQuickAvanceCalls(0),
43 fNoAccurateAdvanceCalls(0),
44 fNoAccurateAdvanceBadSteps(0),
45 fNoAccurateAdvanceGoodSteps(0)
47 if (numComponents != Base::GetStepper()->GetNumberOfVariables())
49 std::ostringstream message;
50 message << "Driver's number of integrated components "
52 << " != Stepper's number of components "
53 << pStepper->GetNumberOfVariables();
54 G4Exception("G4IntegrationDriver","GeomField0002",
55 FatalException, message);
60G4IntegrationDriver<T>::~G4IntegrationDriver()
63 if (fVerboseLevel > 0)
65 G4cout << "G4Integration Driver Stats: "
66 << "#QuickAdvance " << fNoQuickAvanceCalls
67 << " - #AccurateAdvance " << fNoAccurateAdvanceCalls << " "
68 << "#good steps " << fNoAccurateAdvanceGoodSteps << " "
69 << "#bad steps " << fNoAccurateAdvanceBadSteps << G4endl;
75G4double G4IntegrationDriver<T>::AdvanceChordLimited(G4FieldTrack& track,
78 G4double chordDistance)
80 return ChordFinderDelegate::AdvanceChordLimitedImpl(track, stepMax, epsStep,
85void G4IntegrationDriver<T>::OnStartTracking()
87 ChordFinderDelegate::ResetStepEstimate();
90// Runge-Kutta driver with adaptive stepsize control. Integrate starting
91// values at y_current over hstep x2 with accuracy eps.
92// On output ystart is replaced by values at the end of the integration
93// interval. RightHandSide is the right-hand side of ODE system.
94// The source is similar to odeint routine from NRC p.721-722 .
97G4bool G4IntegrationDriver<T>::
98AccurateAdvance(G4FieldTrack& track, G4double hstep,
99 G4double eps, G4double hinitial)
101 ++fNoAccurateAdvanceCalls;
105 std::ostringstream message;
106 message << "Proposed step is zero; hstep = " << hstep << " !";
107 G4Exception("G4IntegrationDriver::AccurateAdvance()",
108 "GeomField1001", JustWarning, message);
114 std::ostringstream message;
115 message << "Invalid run condition." << G4endl
116 << "Proposed step is negative; hstep = " << hstep << "."
118 << "Requested step cannot be negative! Aborting event.";
119 G4Exception("G4IntegrationDriver::AccurateAdvance()",
120 "GeomField0003", EventMustBeAborted, message);
124 G4double hnext, hdid;
126 G4double dydx[G4FieldTrack::ncompSVEC];
127 G4bool succeeded = true, lastStepSucceeded;
129 G4int noFullIntegr = 0, noSmallIntegr = 0;
131 G4double y[G4FieldTrack::ncompSVEC];
132 track.DumpToArray(y);
134 const G4double startCurveLength = track.GetCurveLength();
135 const G4double endCurveLength = startCurveLength + hstep;
136 const G4double hThreshold =
137 std::min(eps * hstep, fSmallestFraction * startCurveLength);
140 if (hinitial > CLHEP::perMillion * hstep && hinitial < hstep)
145 G4double curveLength = startCurveLength;
147 for (G4int nstp = 0; nstp < Base::GetMaxNoSteps(); ++nstp)
149 const G4ThreeVector StartPos =
150 field_utils::makeVector(y, field_utils::Value3D::Position);
152 Base::GetStepper()->RightHandSide(y, dydx);
154 if (h > GetMinimumStep())
156 OneGoodStep(y, dydx, curveLength, h, eps, hdid, hnext);
157 lastStepSucceeded = (hdid == h);
161 G4FieldTrack yFldTrk('0');
162 G4double dchord_step, dyerr, dyerr_len;
163 yFldTrk.LoadFromArray(y, Base::GetStepper()->GetNumberOfVariables());
164 yFldTrk.SetCurveLength(curveLength);
166 QuickAdvance(yFldTrk, dydx, h, dchord_step, dyerr_len);
168 yFldTrk.DumpToArray(y);
172 G4Exception("G4IntegrationDriver::AccurateAdvance()",
173 "GeomField0003", FatalException,
174 "Integration Step became Zero!");
176 dyerr = dyerr_len / h;
179 hnext = Base::ComputeNewStepSize(dyerr / eps, h);
180 lastStepSucceeded = (dyerr <= eps);
183 if (lastStepSucceeded) { ++noFullIntegr; }
184 else { ++noSmallIntegr; }
186 const G4ThreeVector EndPos =
187 field_utils::makeVector(y, field_utils::Value3D::Position);
189 CheckStep(EndPos, StartPos, hdid);
191 // Avoid numerous small last steps
192 if (h < hThreshold || curveLength >= endCurveLength)
197 h = std::max(hnext, GetMinimumStep());
198 if (curveLength + h > endCurveLength)
200 h = endCurveLength - curveLength;
203 // Have we reached the end ?
204 // --> a better test might be x-endCurveLength > an_epsilon
205 succeeded = (curveLength >= endCurveLength);
206 // If it was a "forced" last step
208 track.LoadFromArray(y, Base::GetStepper()->GetNumberOfVariables());
209 track.SetCurveLength(curveLength);
214// Driver for one Runge-Kutta Step with monitoring of local truncation error
215// to ensure accuracy and adjust stepsize. Input are dependent variable
216// array y[0,...,5] and its derivative dydx[0,...,5] at the
217// starting value of the independent variable x . Also input are stepsize
218// to be attempted htry, and the required accuracy eps. On output y and x
219// are replaced by their new values, hdid is the stepsize that was actually
220// accomplished, and hnext is the estimated next stepsize.
221// This is similar to the function rkqs from the book:
222// Numerical Recipes in C: The Art of Scientific Computing (NRC), Second
223// Edition, by William H. Press, Saul A. Teukolsky, William T.
224// Vetterling, and Brian P. Flannery (Cambridge University Press 1992),
225// 16.2 Adaptive StepSize Control for Runge-Kutta, p. 719
228void G4IntegrationDriver<T>::OneGoodStep(G4double y[], // InOut
229 const G4double dydx[],
230 G4double& curveLength, // InOut
232 G4double eps_rel_max,
233 G4double& hdid, // Out
234 G4double& hnext) // Out
237 G4double error2 = DBL_MAX;
239 G4double yerr[G4FieldTrack::ncompSVEC], ytemp[G4FieldTrack::ncompSVEC];
243 static G4ThreadLocal G4int tot_no_trials = 0;
244 const G4int max_trials = 100;
246 for (G4int iter = 0; iter < max_trials; ++iter)
250 Base::GetStepper()->Stepper(y, dydx, h, ytemp, yerr);
251 error2 = field_utils::relativeError2(y, yerr, std::max(h, fMinimumStep),
258 h = Base::ShrinkStepSize2(h, error2);
260 G4double xnew = curveLength + h;
261 if(xnew == curveLength)
263 std::ostringstream message;
264 message << "Stepsize underflow in Stepper !" << G4endl
265 << "- Step's start x=" << curveLength
266 << " and end x= " << xnew
267 << " are equal !! " << G4endl
268 << " Due to step-size= " << h
269 << ". Note that input step was " << htry;
270 G4Exception("G4IntegrationDriver::OneGoodStep()",
271 "GeomField1001", JustWarning, message);
276 hnext = Base::GrowStepSize2(h, error2);
277 curveLength += (hdid = h);
279 field_utils::copy(y, ytemp, Base::GetStepper()->GetNumberOfVariables());
283G4bool G4IntegrationDriver<T>::QuickAdvance(G4FieldTrack& track, // INOUT
284 const G4double dydx[],
286 G4double& dchord_step,
289 ++fNoQuickAvanceCalls;
291 G4double yIn[G4FieldTrack::ncompSVEC],
292 yOut[G4FieldTrack::ncompSVEC],
293 yError[G4FieldTrack::ncompSVEC];
295 track.DumpToArray(yIn);
297 Base::GetStepper()->Stepper(yIn, dydx, hstep, yOut, yError);
299 dchord_step = Base::GetStepper()->DistChord();
300 dyerr = field_utils::absoluteError(yOut, yError, hstep);
301 track.LoadFromArray(yOut, Base::GetStepper()->GetNumberOfVariables());
302 track.SetCurveLength(track.GetCurveLength() + hstep);
308void G4IntegrationDriver<T>::SetSmallestFraction(G4double newFraction)
310 if (newFraction > 1.e-16 && newFraction < 1e-8)
312 fSmallestFraction = newFraction;
316 std::ostringstream message;
317 message << "Smallest Fraction not changed. " << G4endl
318 << " Proposed value was " << newFraction << G4endl
319 << " Value must be between 1.e-8 and 1.e-16";
320 G4Exception("G4IntegrationDriver::SetSmallestFraction()",
321 "GeomField1001", JustWarning, message);
326void G4IntegrationDriver<T>::CheckStep(const G4ThreeVector& posIn,
327 const G4ThreeVector& posOut,
330 const G4double endPointDist = (posOut - posIn).mag();
331 if (endPointDist >= hdid * (1. + CLHEP::perMillion))
333 ++fNoAccurateAdvanceBadSteps;
335 // Issue a warning only for gross differences -
336 // we understand how small difference occur.
337 if (endPointDist >= hdid * (1. + perThousand))
339 G4Exception("G4IntegrationDriver::CheckStep()",
340 "GeomField1002", JustWarning,
341 "endPointDist >= hdid!");
347 ++fNoAccurateAdvanceGoodSteps;
352inline G4double G4IntegrationDriver<T>::GetMinimumStep() const
358void G4IntegrationDriver<T>::SetMinimumStep(G4double minimumStepLength)
360 fMinimumStep = minimumStepLength;
364G4int G4IntegrationDriver<T>::GetVerboseLevel() const
366 return fVerboseLevel;
370void G4IntegrationDriver<T>::SetVerboseLevel(G4int newLevel)
372 fVerboseLevel = newLevel;
376G4double G4IntegrationDriver<T>::GetSmallestFraction() const
378 return fSmallestFraction;
382void G4IntegrationDriver<T>::IncrementQuickAdvanceCalls()
384 ++fNoQuickAvanceCalls;
388void G4IntegrationDriver<T>::StreamInfo( std::ostream& os ) const
390// Write out the parameters / state of the driver
391 os << "State of G4IntegrationDriver: " << std::endl;
392 os << "--Base state (G4RKIntegrationDriver): " << std::endl;
393 Base::StreamInfo( os );
394 os << "--Own state (G4IntegrationDriver<>): " << std::endl;
395 os << " fMinimumStep = " << fMinimumStep << std::endl;
396 os << " Smallest Fraction = " << fSmallestFraction << std::endl;
398 os << " verbose level = " << fVerboseLevel << std::endl;
399 os << " Reintegrates = " << DoesReIntegrate() << std::endl;
400 os << "--Chord Finder Delegate state: " << std::endl;
401 ChordFinderDelegate::StreamDelegateInfo( os );