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// G4ChordFinderDelegate inline methods implementation
29// --------------------------------------------------------------------
33template <class Driver>
34G4ChordFinderDelegate<Driver>::~G4ChordFinderDelegate()
37 if (GetDriver().GetVerboseLevel() > 0)
44template <class Driver>
45void G4ChordFinderDelegate<Driver>::ResetStepEstimate()
47 fLastStepEstimate_Unconstrained = DBL_MAX;
50template <class Driver>
51Driver& G4ChordFinderDelegate<Driver>::GetDriver()
53 return static_cast<Driver&>(*this);
56template <class Driver>
57G4double G4ChordFinderDelegate<Driver>::
58AdvanceChordLimitedImpl(G4FieldTrack& yCurrent, G4double stepMax,
59 G4double epsStep, G4double chordDistance)
62 G4FieldTrack yEnd = yCurrent;
65 const G4double stepPossible = FindNextChord(yCurrent, stepMax,
66 epsStep, chordDistance,
67 yEnd, dyErr, nextStep);
68 if (dyErr < epsStep * stepPossible)
70 // Accept this accuracy.
76 // Advance more accurately to "end of chord"
78 const G4double startCurveLen = yCurrent.GetCurveLength();
79 const G4bool goodAdvance =
80 GetDriver().AccurateAdvance(yCurrent,stepPossible,epsStep,nextStep);
82 return goodAdvance ? stepPossible
83 : yCurrent.GetCurveLength() - startCurveLen;
86// Returns Length of Step taken
89G4double G4ChordFinderDelegate<T>::
90FindNextChord(const G4FieldTrack& yStart,
93 G4double chordDistance,
94 G4FieldTrack& yEnd, // Endpoint
95 G4double& dyErrPos, // Error of endpoint
96 G4double& stepForAccuracy)
98 // 1.) Try to "leap" to end of interval
99 // 2.) Evaluate if resulting chord gives d_chord that is good enough.
100 // 2a.) If d_chord is not good enough, find one that is.
102 G4double dydx[G4FieldTrack::ncompSVEC];
104 G4bool validEndPoint = false;
105 G4double dChordStep, lastStepLength;
107 GetDriver().GetDerivatives(yStart, dydx);
109 const G4double safetyFactor = fFirstFraction; // 0.975 or 0.99 ? was 0.999
111 G4double stepTrial = std::min(stepMax,
112 safetyFactor*fLastStepEstimate_Unconstrained);
114 G4double newStepEst_Uncons = 0.0;
115 G4double stepForChord;
118 constexpr G4int maxTrials = 75; // Avoid endless loop for bad convergence
119 for (; noTrials < maxTrials; ++noTrials)
121 yEnd = yStart; // Always start from initial point
122 GetDriver().QuickAdvance(yEnd, dydx, stepTrial, dChordStep, dyErrPos);
123 lastStepLength = stepTrial;
125 validEndPoint = dChordStep < chordDistance;
126 stepForChord = NewStep(stepTrial, dChordStep,
127 chordDistance, newStepEst_Uncons);
133 if (stepTrial <= 0.0)
135 stepTrial = stepForChord;
137 else if (stepForChord <= stepTrial)
139 // Reduce by a fraction, possibly up to 20%
140 stepTrial = std::min( stepForChord, fFractionLast * stepTrial);
148 if (noTrials >= maxTrials)
150 std::ostringstream message;
151 message << "Exceeded maximum number of trials= " << maxTrials << G4endl
152 << "Current sagita dist= " << dChordStep << G4endl
153 << "Max sagita dist= " << chordDistance << G4endl
154 << "Step sizes (actual and proposed): " << G4endl
155 << "Last trial = " << lastStepLength << G4endl
156 << "Next trial = " << stepTrial << G4endl
157 << "Proposed for chord = " << stepForChord << G4endl;
158 G4Exception("G4ChordFinder::FindNextChord()", "GeomField0003",
159 JustWarning, message);
162 if (newStepEst_Uncons > 0.0)
164 fLastStepEstimate_Unconstrained = newStepEst_Uncons;
167 AccumulateStatistics(noTrials);
170 // Calculate the step size required for accuracy, if it is needed
171 G4double dyErr_relative = dyErrPos / (epsStep * lastStepLength);
172 stepForAccuracy = dyErr_relative > 1 ?
173 GetDriver().ComputeNewStepSize(dyErr_relative, lastStepLength) : 0;
178// Is called to estimate the next step size, even for successful steps,
179// in order to predict an accurate 'chord-sensitive' first step
180// which is likely to assist in more performant 'stepping'.
183G4double G4ChordFinderDelegate<T>::
184NewStep(G4double stepTrialOld,
185 G4double dChordStep, // Curr. dchord achieved
186 G4double fDeltaChord,
187 G4double& stepEstimate_Unconstrained)
191 if (dChordStep > 0.0)
193 stepEstimate_Unconstrained =
194 stepTrialOld * std::sqrt(fDeltaChord / dChordStep);
195 stepTrial = fFractionNextEstimate * stepEstimate_Unconstrained;
199 // Should not update the Unconstrained Step estimate: incorrect!
200 stepTrial = stepTrialOld * 2.;
203 if (stepTrial <= 0.001 * stepTrialOld)
205 if (dChordStep > 1000.0 * fDeltaChord)
207 stepTrial = stepTrialOld * 0.03;
211 if (dChordStep > 100. * fDeltaChord)
213 stepTrial = stepTrialOld * 0.1;
215 else // Try halving the length until dChordStep OK
217 stepTrial = stepTrialOld * 0.5;
221 else if (stepTrial > 1000.0 * stepTrialOld)
223 stepTrial = 1000.0 * stepTrialOld;
226 if (stepTrial == 0.0)
231 // A more sophisticated chord-finder could figure out a better
232 // stepTrial, from dChordStep and the required d_geometry
234 // Calculate R, r_helix (eg at orig point)
235 // if( stepTrial < 2 pi R )
236 // stepTrial = R arc_cos( 1 - fDeltaChord / r_helix )
244void G4ChordFinderDelegate<T>::AccumulateStatistics(G4int noTrials)
246 fTotalNoTrials += noTrials;
249 if (noTrials > fmaxTrials)
251 fmaxTrials = noTrials;
256void G4ChordFinderDelegate<T>::PrintStatistics()
259 G4cout << "G4ChordFinder statistics report: \n"
260 << " No trials: " << fTotalNoTrials
261 << " No Calls: " << fNoCalls
262 << " Max-trial: " << fmaxTrials << "\n"
264 << " fFirstFraction " << fFirstFraction
265 << " fFractionLast " << fFractionLast
266 << " fFractionNextEstimate " << fFractionNextEstimate
271G4int G4ChordFinderDelegate<T>::GetNoCalls()
277G4int G4ChordFinderDelegate<T>::GetNoTrials()
279 return fTotalNoTrials;
283G4int G4ChordFinderDelegate<T>::GetNoMaxTrials()
289void G4ChordFinderDelegate<T>::SetFractions_Last_Next(G4double fractLast,
292 // Use -1.0 as request for Default.
293 if (fractLast == -1.0) fractLast = 1.0; // 0.9;
294 if (fractNext == -1.0) fractNext = 0.98; // 0.9;
296 // fFirstFraction = 0.999; // Safe value, range: ~ 0.95 - 0.999
297 if (GetDriver().GetVerboseLevel() > 0)
299 G4cout << " ChordFnd> Trying to set fractions: "
300 << " first " << fFirstFraction
301 << " last " << fractLast
302 << " next " << fractNext
306 if (fractLast > 0 && fractLast <= 1)
308 fFractionLast = fractLast;
311 std::ostringstream message;
312 message << "Invalid fraction Last = " << fractLast
313 << "; must be 0 < fractionLast <= 1 ";
314 G4Exception("G4ChordFinderDelegate::SetFractions_Last_Next()",
315 "GeomField1001", JustWarning, message);
317 if (fractNext > 0. && fractNext < 1)
319 fFractionNextEstimate = fractNext;
322 std::ostringstream message;
323 message << "Invalid fraction Next = " << fractNext
324 << "; must be 0 < fractionNext < 1 ";
325 G4Exception("G4ChordFinderDelegate::SetFractions_Last_Next()",
326 "GeomField1001", JustWarning, message);
331void G4ChordFinderDelegate<T>::SetFirstFraction(G4double fractFirst)
333 fFirstFraction = fractFirst;
337G4double G4ChordFinderDelegate<T>::GetFirstFraction()
339 return fFirstFraction;
343G4double G4ChordFinderDelegate<T>::GetFractionLast()
345 return fFractionLast;
349G4double G4ChordFinderDelegate<T>::GetFractionNextEstimate()
351 return fFractionNextEstimate;
355G4double G4ChordFinderDelegate<T>::GetLastStepEstimateUnc()
357 return fLastStepEstimate_Unconstrained;
361void G4ChordFinderDelegate<T>::SetLastStepEstimateUnc(G4double stepEst)
363 fLastStepEstimate_Unconstrained = stepEst;
367void G4ChordFinderDelegate<T>::TestChordPrint(G4int noTrials,
370 G4double fDeltaChord,
371 G4double nextStepTrial)
373 G4int oldprec = G4cout.precision(5);
374 G4cout << " ChF/fnc: notrial " << std::setw( 3) << noTrials
375 << " this_step= " << std::setw(10) << lastStepTrial;
376 if( std::fabs( (dChordStep / fDeltaChord) - 1.0 ) < 0.001 )
384 G4cout << " dChordStep= " << std::setw(12) << dChordStep;
385 if( dChordStep > fDeltaChord ) { G4cout << " d+"; }
386 else { G4cout << " d-"; }
388 G4cout << " new_step= " << std::setw(10)
389 << fLastStepEstimate_Unconstrained
390 << " new_step_constr= " << std::setw(10)
391 << lastStepTrial << G4endl;
392 G4cout << " nextStepTrial = " << std::setw(10) << nextStepTrial << G4endl;
393 G4cout.precision(oldprec);
397void G4ChordFinderDelegate<T>::StreamDelegateInfo( std::ostream& os ) const
399// Write out the parameters / state of the driver
400 os << "State of G4ChordFinderDelegate: " << std::endl;
401 os << "--Parameters: " << std::endl;
402 os << " First Fraction = " << fFirstFraction << std::endl;
403 os << " Last Fraction = " << fFractionLast << std::endl;
404 os << " Fract Next est = " << fFractionNextEstimate << std::endl;
406 os << "--State (fungible): " << std::endl;
407 os << " Maximum No Trials (seen) = " << fmaxTrials << std::endl;
408 os << " LastStepEstimate (Unconstrained) = " << fLastStepEstimate_Unconstrained
410 // os << " Statistics NOT printed. " << std::endl;
411 os << "--Statistics: trials= " << fTotalNoTrials
412 << " calls= " << fNoCalls << std::endl;