// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // G4ChordFinderDelegate inline methods implementation // // Author: Dmitry Sorokin (CERN, Google Summer of Code 2017), 12.09.2018 // -------------------------------------------------------------------- template G4ChordFinderDelegate::~G4ChordFinderDelegate() { #ifdef G4VERBOSE if (GetDriver().GetVerboseLevel() > 0) { PrintStatistics(); } #endif } template void G4ChordFinderDelegate::ResetStepEstimate() { fLastStepEstimate_Unconstrained = DBL_MAX; } template Driver& G4ChordFinderDelegate::GetDriver() { return static_cast(*this); } template G4double G4ChordFinderDelegate:: AdvanceChordLimitedImpl(G4FieldTrack& yCurrent, G4double stepMax, G4double epsStep, G4double chordDistance) { G4double dyErr; G4FieldTrack yEnd = yCurrent; G4double nextStep; const G4double stepPossible = FindNextChord(yCurrent, stepMax, epsStep, chordDistance, yEnd, dyErr, nextStep); if (dyErr < epsStep * stepPossible) { // Accept this accuracy. // yCurrent = yEnd; return stepPossible; } // Advance more accurately to "end of chord" // const G4double startCurveLen = yCurrent.GetCurveLength(); const G4bool goodAdvance = GetDriver().AccurateAdvance(yCurrent,stepPossible,epsStep,nextStep); return goodAdvance ? stepPossible : yCurrent.GetCurveLength() - startCurveLen; } // Returns Length of Step taken // template G4double G4ChordFinderDelegate:: FindNextChord(const G4FieldTrack& yStart, G4double stepMax, G4double epsStep, G4double chordDistance, G4FieldTrack& yEnd, // Endpoint G4double& dyErrPos, // Error of endpoint G4double& stepForAccuracy) { // 1.) Try to "leap" to end of interval // 2.) Evaluate if resulting chord gives d_chord that is good enough. // 2a.) If d_chord is not good enough, find one that is. G4double dydx[G4FieldTrack::ncompSVEC]; G4bool validEndPoint = false; G4double dChordStep, lastStepLength; GetDriver().GetDerivatives(yStart, dydx); const G4double safetyFactor = fFirstFraction; // 0.975 or 0.99 ? was 0.999 G4double stepTrial = std::min(stepMax, safetyFactor*fLastStepEstimate_Unconstrained); G4double newStepEst_Uncons = 0.0; G4double stepForChord; G4int noTrials = 1; constexpr G4int maxTrials = 75; // Avoid endless loop for bad convergence for (; noTrials < maxTrials; ++noTrials) { yEnd = yStart; // Always start from initial point GetDriver().QuickAdvance(yEnd, dydx, stepTrial, dChordStep, dyErrPos); lastStepLength = stepTrial; validEndPoint = dChordStep < chordDistance; stepForChord = NewStep(stepTrial, dChordStep, chordDistance, newStepEst_Uncons); if (validEndPoint) { break; } if (stepTrial <= 0.0) { stepTrial = stepForChord; } else if (stepForChord <= stepTrial) { // Reduce by a fraction, possibly up to 20% stepTrial = std::min( stepForChord, fFractionLast * stepTrial); } else { stepTrial *= 0.1; } } if (noTrials >= maxTrials) { std::ostringstream message; message << "Exceeded maximum number of trials= " << maxTrials << G4endl << "Current sagita dist= " << dChordStep << G4endl << "Max sagita dist= " << chordDistance << G4endl << "Step sizes (actual and proposed): " << G4endl << "Last trial = " << lastStepLength << G4endl << "Next trial = " << stepTrial << G4endl << "Proposed for chord = " << stepForChord << G4endl; G4Exception("G4ChordFinder::FindNextChord()", "GeomField0003", JustWarning, message); } if (newStepEst_Uncons > 0.0) { fLastStepEstimate_Unconstrained = newStepEst_Uncons; } AccumulateStatistics(noTrials); // Calculate the step size required for accuracy, if it is needed G4double dyErr_relative = dyErrPos / (epsStep * lastStepLength); stepForAccuracy = dyErr_relative > 1 ? GetDriver().ComputeNewStepSize(dyErr_relative, lastStepLength) : 0; return stepTrial; } // Is called to estimate the next step size, even for successful steps, // in order to predict an accurate 'chord-sensitive' first step // which is likely to assist in more performant 'stepping'. // template G4double G4ChordFinderDelegate:: NewStep(G4double stepTrialOld, G4double dChordStep, // Curr. dchord achieved G4double fDeltaChord, G4double& stepEstimate_Unconstrained) { G4double stepTrial; if (dChordStep > 0.0) { stepEstimate_Unconstrained = stepTrialOld * std::sqrt(fDeltaChord / dChordStep); stepTrial = fFractionNextEstimate * stepEstimate_Unconstrained; } else { // Should not update the Unconstrained Step estimate: incorrect! stepTrial = stepTrialOld * 2.; } if (stepTrial <= 0.001 * stepTrialOld) { if (dChordStep > 1000.0 * fDeltaChord) { stepTrial = stepTrialOld * 0.03; } else { if (dChordStep > 100. * fDeltaChord) { stepTrial = stepTrialOld * 0.1; } else // Try halving the length until dChordStep OK { stepTrial = stepTrialOld * 0.5; } } } else if (stepTrial > 1000.0 * stepTrialOld) { stepTrial = 1000.0 * stepTrialOld; } if (stepTrial == 0.0) { stepTrial= 0.000001; } // A more sophisticated chord-finder could figure out a better // stepTrial, from dChordStep and the required d_geometry // e.g. // Calculate R, r_helix (eg at orig point) // if( stepTrial < 2 pi R ) // stepTrial = R arc_cos( 1 - fDeltaChord / r_helix ) // else // ?? return stepTrial; } template void G4ChordFinderDelegate::AccumulateStatistics(G4int noTrials) { fTotalNoTrials += noTrials; ++fNoCalls; if (noTrials > fmaxTrials) { fmaxTrials = noTrials; } } template void G4ChordFinderDelegate::PrintStatistics() { // Print Statistics G4cout << "G4ChordFinder statistics report: \n" << " No trials: " << fTotalNoTrials << " No Calls: " << fNoCalls << " Max-trial: " << fmaxTrials << "\n" << " Parameters: " << " fFirstFraction " << fFirstFraction << " fFractionLast " << fFractionLast << " fFractionNextEstimate " << fFractionNextEstimate << G4endl; } template G4int G4ChordFinderDelegate::GetNoCalls() { return fNoCalls; } template G4int G4ChordFinderDelegate::GetNoTrials() { return fTotalNoTrials; } template G4int G4ChordFinderDelegate::GetNoMaxTrials() { return fmaxTrials; } template void G4ChordFinderDelegate::SetFractions_Last_Next(G4double fractLast, G4double fractNext) { // Use -1.0 as request for Default. if (fractLast == -1.0) { fractLast = 1.0; } // 0.9; if (fractNext == -1.0) { fractNext = 0.98; } // 0.9; // fFirstFraction = 0.999; // Safe value, range: ~ 0.95 - 0.999 if (GetDriver().GetVerboseLevel() > 0) { G4cout << " ChordFnd> Trying to set fractions: " << " first " << fFirstFraction << " last " << fractLast << " next " << fractNext << G4endl; } if (fractLast > 0 && fractLast <= 1) { fFractionLast = fractLast; } else { std::ostringstream message; message << "Invalid fraction Last = " << fractLast << "; must be 0 < fractionLast <= 1 "; G4Exception("G4ChordFinderDelegate::SetFractions_Last_Next()", "GeomField1001", JustWarning, message); } if (fractNext > 0. && fractNext < 1) { fFractionNextEstimate = fractNext; } else { std::ostringstream message; message << "Invalid fraction Next = " << fractNext << "; must be 0 < fractionNext < 1 "; G4Exception("G4ChordFinderDelegate::SetFractions_Last_Next()", "GeomField1001", JustWarning, message); } } template void G4ChordFinderDelegate::SetFirstFraction(G4double fractFirst) { fFirstFraction = fractFirst; } template G4double G4ChordFinderDelegate::GetFirstFraction() { return fFirstFraction; } template G4double G4ChordFinderDelegate::GetFractionLast() { return fFractionLast; } template G4double G4ChordFinderDelegate::GetFractionNextEstimate() { return fFractionNextEstimate; } template G4double G4ChordFinderDelegate::GetLastStepEstimateUnc() { return fLastStepEstimate_Unconstrained; } template void G4ChordFinderDelegate::SetLastStepEstimateUnc(G4double stepEst) { fLastStepEstimate_Unconstrained = stepEst; } template void G4ChordFinderDelegate::TestChordPrint(G4int noTrials, G4int lastStepTrial, G4double dChordStep, G4double fDeltaChord, G4double nextStepTrial) { G4int oldprec = G4cout.precision(5); G4cout << " ChF/fnc: notrial " << std::setw( 3) << noTrials << " this_step= " << std::setw(10) << lastStepTrial; if( std::fabs( (dChordStep / fDeltaChord) - 1.0 ) < 0.001 ) { G4cout.precision(8); } else { G4cout.precision(6); } G4cout << " dChordStep= " << std::setw(12) << dChordStep; if( dChordStep > fDeltaChord ) { G4cout << " d+"; } else { G4cout << " d-"; } G4cout.precision(5); G4cout << " new_step= " << std::setw(10) << fLastStepEstimate_Unconstrained << " new_step_constr= " << std::setw(10) << lastStepTrial << G4endl; G4cout << " nextStepTrial = " << std::setw(10) << nextStepTrial << G4endl; G4cout.precision(oldprec); } template void G4ChordFinderDelegate::StreamDelegateInfo( std::ostream& os ) const { // Write out the parameters / state of the driver os << "State of G4ChordFinderDelegate: " << std::endl; os << "--Parameters: " << std::endl; os << " First Fraction = " << fFirstFraction << std::endl; os << " Last Fraction = " << fFractionLast << std::endl; os << " Fract Next est = " << fFractionNextEstimate << std::endl; os << "--State (fungible): " << std::endl; os << " Maximum No Trials (seen) = " << fmaxTrials << std::endl; os << " LastStepEstimate (Unconstrained) = " << fLastStepEstimate_Unconstrained << std::endl; // os << " Statistics NOT printed. " << std::endl; os << "--Statistics: trials= " << fTotalNoTrials << " calls= " << fNoCalls << std::endl; }