Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-15 09:09:23

0001 //
0002 // ********************************************************************
0003 // * License and Disclaimer                                           *
0004 // *                                                                  *
0005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
0006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
0007 // * conditions of the Geant4 Software License,  included in the file *
0008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
0009 // * include a list of copyright holders.                             *
0010 // *                                                                  *
0011 // * Neither the authors of this software system, nor their employing *
0012 // * institutes,nor the agencies providing financial support for this *
0013 // * work  make  any representation or  warranty, express or implied, *
0014 // * regarding  this  software system or assume any liability for its *
0015 // * use.  Please see the license in the file  LICENSE  and URL above *
0016 // * for the full disclaimer and the limitation of liability.         *
0017 // *                                                                  *
0018 // * This  code  implementation is the result of  the  scientific and *
0019 // * technical work of the GEANT4 collaboration.                      *
0020 // * By using,  copying,  modifying or  distributing the software (or *
0021 // * any work based  on the software)  you  agree  to acknowledge its *
0022 // * use  in  resulting  scientific  publications,  and indicate your *
0023 // * acceptance of all terms of the Geant4 Software license.          *
0024 // ********************************************************************
0025 //
0026 // G4InterpolationDriver inline implementation
0027 //
0028 // Author: Dmitry Sorokin (CERN), 26.09.2018
0029 // --------------------------------------------------------------------
0030 
0031 #include "G4Exception.hh"
0032 #include "G4FieldUtils.hh"
0033 #include "G4LineSection.hh"
0034 
0035 #include <CLHEP/Units/SystemOfUnits.h>
0036 
0037 #include <algorithm>
0038 
0039 template <class T, G4bool StepperCachesDchord>
0040 G4InterpolationDriver<T, StepperCachesDchord>::G4InterpolationDriver(
0041   G4double hminimum, T* pStepper, G4int numComponents, G4int statisticsVerbose)
0042   : G4RKIntegrationDriver<T>(pStepper), fMinimumStep(hminimum), fVerboseLevel(statisticsVerbose)
0043 {
0044   if (numComponents != Base::GetStepper()->GetNumberOfVariables())
0045   {
0046     std::ostringstream message;
0047     message << "Driver's number of integrated components " << numComponents
0048             << " != Stepper's number of components " << pStepper->GetNumberOfVariables();
0049     G4Exception("G4InterpolationDriver", "GeomField0002", FatalException, message);
0050   }
0051 
0052   for (G4int i = 0; i < Base::GetMaxNoSteps(); ++i)
0053   {
0054     fSteppers.push_back(
0055       {std::unique_ptr<T>(
0056          new T(pStepper->GetSpecificEquation(),  // Interpolating stepper must have this!
0057            pStepper->GetNumberOfVariables())),
0058         DBL_MAX, -DBL_MAX, 0.0});
0059   }
0060 
0061   fLastStepper = fSteppers.end();
0062 }
0063 
0064 template <class T, G4bool StepperCachesDchord>
0065 G4InterpolationDriver<T, StepperCachesDchord>::~G4InterpolationDriver()
0066 {
0067 #ifdef G4VERBOSE
0068   if (fVerboseLevel > 0)
0069   {
0070     G4cout << "G4ChordFinder statistics report: \n"
0071            << "  No trials: " << fTotalNoTrials << "  No Calls: " << fNoCalls
0072            << "  Max-trial: " << fmaxTrials << G4endl;
0073   }
0074 #endif
0075 }
0076 
0077 template <class T, G4bool StepperCachesDchord>
0078 void G4InterpolationDriver<T, StepperCachesDchord>::OnStartTracking()
0079 {
0080   fChordStepEstimate = DBL_MAX;
0081   fhnext = DBL_MAX;
0082   fTotalStepsForTrack = 0;
0083 }
0084 
0085 template <class T, G4bool StepperCachesDchord>
0086 void G4InterpolationDriver<T, StepperCachesDchord>::OnComputeStep(const G4FieldTrack* /*track*/)
0087 {
0088   fKeepLastStepper = false;
0089   fFirstStep = true;
0090   fLastStepper = fSteppers.end();
0091 }
0092 
0093 template <class T, G4bool StepperCachesDchord>
0094 void G4InterpolationDriver<T, StepperCachesDchord>::SetVerboseLevel(G4int level)
0095 {
0096   fVerboseLevel = level;
0097 }
0098 
0099 template <class T, G4bool StepperCachesDchord>
0100 G4int G4InterpolationDriver<T, StepperCachesDchord>::GetVerboseLevel() const
0101 {
0102   return fVerboseLevel;
0103 }
0104 
0105 template <class T, G4bool StepperCachesDchord>
0106 void G4InterpolationDriver<T, StepperCachesDchord>::Interpolate(
0107   G4double curveLength, field_utils::State& y) const
0108 {
0109   if (fLastStepper == fSteppers.end())
0110   {
0111     std::ostringstream message;
0112     message << "LOGICK ERROR: fLastStepper == end";
0113     G4Exception("G4InterpolationDriver::Interpolate()", "GeomField1001", FatalException, message);
0114     return;
0115   }
0116 
0117   ConstStepperIterator end = fLastStepper + 1;
0118 
0119   auto it = std::lower_bound(fSteppers.cbegin(), end, curveLength,
0120     [](const InterpStepper& stepper, G4double value) { return stepper.end < value; });
0121   if (it == end)
0122   {
0123     if (curveLength - fLastStepper->end > CLHEP::perMillion)
0124     {
0125       std::ostringstream message;
0126       message << "curveLength = " << curveLength << " > " << fLastStepper->end;
0127       G4Exception("G4InterpolationDriver::Interpolate()", "GeomField1001", JustWarning, message);
0128     }
0129 
0130     return fLastStepper->stepper->Interpolate(1, y);
0131   }
0132 
0133   if (curveLength < it->begin)
0134   {
0135     if (it->begin - curveLength > CLHEP::perMillion)
0136     {
0137       std::ostringstream message;
0138       message << "curveLength = " << curveLength << " < " << it->begin;
0139       G4Exception("G4InterpolationDriver::Interpolate()", "GeomField1001", JustWarning, message);
0140     }
0141 
0142     return it->stepper->Interpolate(0, y);
0143   }
0144 
0145   return InterpolateImpl(curveLength, it, y);
0146 }
0147 
0148 template <class T, G4bool StepperCachesDchord>
0149 void G4InterpolationDriver<T, StepperCachesDchord>::InterpolateImpl(
0150   G4double curveLength, ConstStepperIterator it, field_utils::State& y) const
0151 {
0152   const G4double tau = (curveLength - it->begin) * it->inverseLength;
0153   return it->stepper->Interpolate(field_utils::clamp(tau, 0., 1.), y);
0154 }
0155 
0156 template <class T, G4bool StepperCachesDchord>
0157 G4double G4InterpolationDriver<T, StepperCachesDchord>::DistChord(const field_utils::State& yBegin,
0158   G4double curveLengthBegin, const field_utils::State& yEnd, G4double curveLengthEnd) const
0159 {
0160   if (StepperCachesDchord)
0161   {
0162     // optimization check if it worth
0163     //
0164     if (curveLengthBegin == fLastStepper->begin && curveLengthEnd == fLastStepper->end)
0165     {
0166       return fLastStepper->stepper
0167         ->DistChord();  // QssStepper Returns 0.0  !???  Not implemented => WRONG
0168     }
0169   }
0170 
0171   const G4double curveLengthMid = 0.5 * (curveLengthBegin + curveLengthEnd);
0172   field_utils::State yMid;
0173 
0174   Interpolate(curveLengthMid, yMid);
0175 
0176   return G4LineSection::Distline(field_utils::makeVector(yMid, field_utils::Value3D::Position),
0177     field_utils::makeVector(yBegin, field_utils::Value3D::Position),
0178     field_utils::makeVector(yEnd, field_utils::Value3D::Position));
0179 }
0180 
0181 template <class T, G4bool StepperCachesDchord>
0182 G4double G4InterpolationDriver<T, StepperCachesDchord>::AdvanceChordLimited(
0183   G4FieldTrack& track, G4double hstep, G4double epsStep, G4double chordDistance)
0184 {
0185   ++fTotalStepsForTrack;
0186 
0187   const G4double curveLengthBegin = track.GetCurveLength();
0188   const G4double hend = std::min(hstep, fChordStepEstimate);
0189   G4double hdid = 0.0;
0190   auto it = fSteppers.begin();
0191   G4double dChordStep = 0.0;
0192 
0193   field_utils::State yBegin, y;
0194   track.DumpToArray(yBegin);
0195   track.DumpToArray(y);
0196 
0197   if (fFirstStep)
0198   {
0199     Base::GetEquationOfMotion()->RightHandSide(y, fdydx);
0200     fFirstStep = false;
0201   }
0202 
0203   if (fKeepLastStepper)
0204   {
0205     std::swap(*fSteppers.begin(), *fLastStepper);
0206     it = fSteppers.begin();  // new begin, update iterator
0207     fLastStepper = it;
0208     hdid = it->end - curveLengthBegin;
0209     if (hdid > hend)
0210     {
0211       hdid = hend;
0212       InterpolateImpl(curveLengthBegin + hdid, it, y);
0213     }
0214     else
0215     {
0216       field_utils::copy(y, it->stepper->GetYOut());
0217     }
0218 
0219     dChordStep = DistChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid);
0220 
0221     ++it;
0222   }
0223 
0224   // accurate advance & check chord distance
0225   G4double h = fhnext;
0226   for (; hdid < hend && dChordStep < chordDistance && it != fSteppers.end(); ++it)
0227   {
0228     h = std::min(h, hstep - hdid);
0229 
0230     // make one step
0231     hdid += OneGoodStep(it, y, fdydx, h, epsStep, curveLengthBegin + hdid, &track);
0232 
0233     // update last stepper
0234     fLastStepper = it;
0235 
0236     // estimate chord distance
0237     dChordStep =
0238       std::max(dChordStep, DistChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid));
0239   }
0240 
0241   // Now, either
0242   //   - full integration ( hdid >= hend )
0243   //   - estimated chord has exceeded limit 'chordDistance'
0244   //   - reached maximum number of steps (from number of steppers.)
0245 
0246   // update step estimation
0247   if (h > fMinimumStep)
0248   {
0249     fhnext = h;
0250   }
0251 
0252   // CheckState();
0253 
0254   // update chord step estimate
0255   //
0256   hdid =
0257     FindNextChord(yBegin, curveLengthBegin, y, curveLengthBegin + hdid, dChordStep, chordDistance);
0258 
0259   const G4double curveLengthEnd = curveLengthBegin + hdid;
0260   fKeepLastStepper = fLastStepper->end - curveLengthEnd > CLHEP::perMillion;
0261   track.LoadFromArray(y, fLastStepper->stepper->GetNumberOfVariables());
0262   track.SetCurveLength(curveLengthBegin + hdid);
0263 
0264   return hdid;
0265 }
0266 
0267 template <class T, G4bool StepperCachesDchord>
0268 G4double G4InterpolationDriver<T, StepperCachesDchord>::FindNextChord(
0269   const field_utils::State& yBegin, G4double curveLengthBegin, field_utils::State& yEnd,
0270   G4double curveLengthEnd, G4double dChord, G4double chordDistance)
0271 {
0272   G4double hstep = curveLengthEnd - curveLengthBegin;
0273   G4double curveLength = curveLengthEnd;
0274 
0275   G4int i = 1;
0276   for (; i < fMaxTrials && dChord > chordDistance && curveLength > fLastStepper->begin; ++i)
0277   {
0278     // crop step size
0279     hstep = CalcChordStep(hstep, dChord, chordDistance);
0280 
0281     // hstep should be in the last stepper
0282     hstep = std::max(hstep, fLastStepper->begin - curveLengthBegin);
0283     curveLength = curveLengthBegin + hstep;
0284 
0285     // use fLastStepper!
0286     InterpolateImpl(curveLength, fLastStepper, yEnd);
0287 
0288     // update chord distance
0289     dChord = DistChord(yBegin, curveLengthBegin, yEnd, curveLength);
0290   }
0291 
0292   // dChord may be zero
0293   //
0294   if (dChord > 0.0)
0295   {
0296     fChordStepEstimate = hstep * std::sqrt(chordDistance / dChord);
0297   }
0298 
0299   if (i == fMaxTrials)
0300   {
0301     G4Exception(
0302       "G4InterpolationDriver::FindNextChord()", "GeomField1001", JustWarning, "cannot converge");
0303   }
0304 
0305   AccumulateStatistics(i);
0306 
0307   return hstep;
0308 }
0309 
0310 // Is called to estimate the next step size, even for successful steps,
0311 // in order to predict an accurate 'chord-sensitive' first step
0312 // which is likely to assist in more performant 'stepping'.
0313 //
0314 template <class T, G4bool StepperCachesDchord>
0315 G4double G4InterpolationDriver<T, StepperCachesDchord>::CalcChordStep(
0316   G4double stepTrialOld, G4double dChordStep, G4double chordDistance)
0317 {
0318   const G4double chordStepEstimate = stepTrialOld * std::sqrt(chordDistance / dChordStep);
0319   G4double stepTrial = fFractionNextEstimate * chordStepEstimate;
0320 
0321   if (stepTrial <= 0.001 * stepTrialOld)
0322   {
0323     if (dChordStep > 1000.0 * chordDistance)
0324     {
0325       stepTrial = stepTrialOld * 0.03;
0326     }
0327     else
0328     {
0329       if (dChordStep > 100. * chordDistance)
0330       {
0331         stepTrial = stepTrialOld * 0.1;
0332       }
0333       else  // Try halving the length until dChordStep OK
0334       {
0335         stepTrial = stepTrialOld * 0.5;
0336       }
0337     }
0338   }
0339   else if (stepTrial > 1000.0 * stepTrialOld)
0340   {
0341     stepTrial = 1000.0 * stepTrialOld;
0342   }
0343 
0344   if (stepTrial == 0.0)
0345   {
0346     stepTrial = 0.000001;
0347   }
0348 
0349   // A more sophisticated chord-finder could figure out a better
0350   // stepTrial, from dChordStep and the required d_geometry
0351   //   e.g.
0352   //      Calculate R, r_helix (eg at orig point)
0353   //      if( stepTrial < 2 pi  R )
0354   //          stepTrial = R arc_cos( 1 - chordDistance / r_helix )
0355   //      else
0356   //          ??
0357 
0358   return stepTrial;
0359 }
0360 
0361 template <class T, G4bool StepperCachesDchord>
0362 G4bool G4InterpolationDriver<T, StepperCachesDchord>::AccurateAdvance(
0363   G4FieldTrack& track, G4double hstep, G4double /*eps*/, G4double /*hinitial*/
0364 )
0365 {
0366   if (hstep == 0.0)
0367   {
0368     std::ostringstream message;
0369     message << "Proposed step is zero; hstep = " << hstep << " !";
0370     G4Exception("G4InterpolationDriver::AccurateAdvance()", "GeomField1001", JustWarning, message);
0371     return true;
0372   }
0373 
0374   if (hstep < 0)
0375   {
0376     std::ostringstream message;
0377     message << "Invalid run condition." << G4endl << "Proposed step is negative; hstep = " << hstep
0378             << "." << G4endl << "Requested step cannot be negative! Aborting event.";
0379     G4Exception(
0380       "G4InterpolationDriver::AccurateAdvance()", "GeomField0003", EventMustBeAborted, message);
0381     return false;
0382   }
0383 
0384   const G4double curveLength = track.GetCurveLength();
0385   const G4double curveLengthEnd = curveLength + hstep;
0386 
0387   field_utils::State y;
0388   Interpolate(curveLengthEnd, y);
0389 
0390   track.LoadFromArray(y, Base::GetStepper()->GetNumberOfVariables());
0391   track.SetCurveLength(curveLengthEnd);
0392 
0393   return true;
0394 }
0395 
0396 // Driver for one Runge-Kutta Step with monitoring of local truncation error
0397 // to ensure accuracy and adjust stepsize. Input are dependent variable
0398 // array y[0,...,5] and its derivative dydx[0,...,5] at the
0399 // starting value of the independent variable x . Also input are stepsize
0400 // to be attempted htry, and the required accuracy eps. On output y and x
0401 // are replaced by their new values, hdid is the stepsize that was actually
0402 // accomplished, and hnext is the estimated next stepsize.
0403 // This is similar to the function rkqs from the book:
0404 // Numerical Recipes in C: The Art of Scientific Computing (NRC), Second
0405 // Edition, by William H. Press, Saul A. Teukolsky, William T.
0406 // Vetterling, and Brian P. Flannery (Cambridge University Press 1992),
0407 // 16.2 Adaptive StepSize Control for Runge-Kutta, p. 719
0408 //
0409 template <class T, G4bool StepperCachesDchord>
0410 G4double G4InterpolationDriver<T, StepperCachesDchord>::OneGoodStep(StepperIterator it,
0411   field_utils::State& y, field_utils::State& dydx, G4double& hstep, G4double epsStep,
0412   G4double curveLength, G4FieldTrack* /*track*/)
0413 
0414 {
0415   G4double error2 = DBL_MAX;
0416   field_utils::State yerr, ytemp, dydxtemp;
0417   G4double h = hstep;
0418 
0419   G4int i = 0;
0420   for (; i < fMaxTrials; ++i)
0421   {
0422     it->stepper->Stepper(y, dydx, h, ytemp, yerr, dydxtemp);
0423     error2 = field_utils::relativeError2(y, yerr, h, epsStep);
0424 
0425     if (error2 <= 1.0)
0426     {
0427       hstep = std::max(Base::GrowStepSize2(h, error2), fMinimumStep);
0428       break;
0429     }
0430 
0431     // don't control error for small steps
0432     if (h <= fMinimumStep)
0433     {
0434       hstep = fMinimumStep;
0435       break;
0436     }
0437 
0438     h = std::max(Base::ShrinkStepSize2(h, error2), fMinimumStep);
0439   }
0440 
0441   if (i == fMaxTrials)
0442   {
0443     G4Exception(
0444       "G4InterpolationDriver::OneGoodStep()", "GeomField1001", JustWarning, "cannot converge");
0445     hstep = std::max(Base::ShrinkStepSize2(h, error2), fMinimumStep);
0446   }
0447 
0448   // set interpolation inverval
0449   it->begin = curveLength;
0450   it->end = curveLength + h;
0451   it->inverseLength = 1. / h;
0452 
0453   // setup interpolation
0454   it->stepper->SetupInterpolation();
0455 
0456   field_utils::copy(dydx, dydxtemp);
0457   field_utils::copy(y, ytemp);
0458 
0459   return h;
0460 }
0461 
0462 template <class T, G4bool StepperCachesDchord>
0463 void G4InterpolationDriver<T, StepperCachesDchord>::PrintState() const
0464 {
0465   using namespace field_utils;
0466   State prevEnd, currBegin;
0467   auto prev = fSteppers.begin();
0468 
0469   G4cout << "====== curr state ========" << G4endl;
0470   for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
0471   {
0472     i->stepper->Interpolate(0, currBegin);
0473 
0474     G4cout << "cl_begin: " << i->begin << " "
0475            << "cl_end: " << i->end << " ";
0476 
0477     if (prev != i)
0478     {
0479       prev->stepper->Interpolate(1, prevEnd);
0480       auto prevPos = makeVector(prevEnd, Value3D::Position);
0481       auto currPos = makeVector(currBegin, Value3D::Position);
0482       G4cout << "diff_begin: " << (prevPos - currPos).mag();
0483     }
0484 
0485     G4cout << G4endl;
0486     prev = i;
0487   }
0488 
0489   const G4double clBegin = fSteppers.begin()->begin;
0490   const G4double clEnd = fLastStepper->end;
0491   const G4double hstep = (clEnd - clBegin) / 10.;
0492   State yBegin, yCurr;
0493   Interpolate(0, yBegin);
0494   for (G4double cl = clBegin; cl <= clEnd + 1e-12; cl += hstep)
0495   {
0496     Interpolate(cl, yCurr);
0497     auto d = DistChord(yBegin, clBegin, yCurr, cl);
0498     G4cout << "cl: " << cl << " chord_distance: " << d << G4endl;
0499   }
0500 
0501   G4cout << "==========================" << G4endl;
0502 }
0503 
0504 template <class T, G4bool StepperCachesDchord>
0505 void G4InterpolationDriver<T, StepperCachesDchord>::CheckState() const
0506 {
0507   G4int smallSteps = 0;
0508   for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
0509   {
0510     G4double stepLength = i->end - i->begin;
0511     if (stepLength < fMinimumStep)
0512     {
0513       ++smallSteps;
0514     }
0515   }
0516 
0517   if (smallSteps > 1)
0518   {
0519     std::ostringstream message;
0520     message << "====== curr state ========\n";
0521     for (auto i = fSteppers.begin(); i <= fLastStepper; ++i)
0522     {
0523       message << "cl_begin: " << i->begin << " "
0524               << "cl_end: " << i->end << "\n";
0525     }
0526 
0527     G4Exception("G4InterpolationDriver::CheckState()", "GeomField0003", FatalException, message);
0528   }
0529 }
0530 
0531 template <class T, G4bool StepperCachesDchord>
0532 void G4InterpolationDriver<T, StepperCachesDchord>::AccumulateStatistics(G4int noTrials)
0533 {
0534   fTotalNoTrials += noTrials;
0535   ++fNoCalls;
0536 
0537   if (noTrials > fmaxTrials)
0538   {
0539     fmaxTrials = noTrials;
0540   }
0541 }
0542 
0543 template <class T, G4bool StepperCachesDchord>
0544 void G4InterpolationDriver<T, StepperCachesDchord>::StreamInfo(std::ostream& os) const
0545 {
0546   os << "State of G4InterpolationDriver: " << std::endl;
0547   os << "--Base state (G4RKIntegrationDriver): " << std::endl;
0548   Base::StreamInfo(os);
0549   os << "  fMinimumStep   =      " << fMinimumStep << std::endl;
0550   // os << "  Max number of Steps = " << fMaxNoSteps << std::endl;
0551   // os << "  Safety factor       = " << safety  << std::endl;
0552   // os << "  Power - shrink      = " << pshrnk << std::endl;
0553   // os << "  Power - grow        = " << pgrow << std::endl;
0554   // os << "  threshold - shrink  = " << errorConstraintShrink << std::endl;
0555   // os << "  threshold - grow    = " << errorConstraintGrow   << std::endl;
0556 
0557   os << "  Max num of Trials   = " << fMaxTrials << std::endl;
0558   os << "  Fract Next Estimate = " << fFractionNextEstimate << std::endl;
0559   os << "  Smallest Curve Fract= " << fSmallestCurveFraction << std::endl;
0560 
0561   os << "  VerboseLevel        = " << fVerboseLevel << std::endl;
0562   os << "  KeepLastStepper     = " << fKeepLastStepper << std::endl;
0563 }