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// G4FSALIntegrationDriver inline implementation
 
   28// Created: D.Sorokin, 2017
 
   29// --------------------------------------------------------------------
 
   31#include "G4FieldUtils.hh"
 
   34G4FSALIntegrationDriver<T>::
 
   35G4FSALIntegrationDriver ( G4double hminimum, T* pStepper,
 
   36                          G4int numComponents, G4int statisticsVerbose )
 
   38      fMinimumStep(hminimum),
 
   39      fSmallestFraction(1e-12),
 
   40      fVerboseLevel(statisticsVerbose),
 
   41      fNoQuickAvanceCalls(0),
 
   42      fNoAccurateAdvanceCalls(0),
 
   43      fNoAccurateAdvanceBadSteps(0),
 
   44      fNoAccurateAdvanceGoodSteps(0)
 
   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("G4FSALIntegrationDriver","GeomField0002",
 
   54                    FatalException, message);
 
   59G4FSALIntegrationDriver<T>::~G4FSALIntegrationDriver()
 
   62    if( fVerboseLevel > 0 )
 
   63       G4cout << "G4FSALIntegration Driver Stats: "
 
   64              << "#QuickAdvance " << fNoQuickAvanceCalls
 
   65              << " - #AccurateAdvance " << fNoAccurateAdvanceCalls << G4endl
 
   66              << "#good steps " << fNoAccurateAdvanceGoodSteps << " "
 
   67              << "#bad steps " << fNoAccurateAdvanceBadSteps << G4endl;
 
   71// Runge-Kutta driver with adaptive stepsize control. Integrate starting
 
   72// values at y_current over hstep x2 with accuracy eps.
 
   73// On output ystart is replaced by values at the end of the integration
 
   74// interval. RightHandSide is the right-hand side of ODE system.
 
   75// The source is similar to odeint routine from NRC p.721-722 .
 
   78G4bool G4FSALIntegrationDriver<T>::
 
   79AccurateAdvance( G4FieldTrack& track, G4double hstep,
 
   80                 G4double eps, G4double hinitial )
 
   82    ++fNoAccurateAdvanceCalls;
 
   84    if (hstep < GetMinimumStep())
 
   86        G4double dchord_step = 0.0, dyerr = 0.0;
 
   87        G4double dydx[G4FieldTrack::ncompSVEC];
 
   88        Base::GetDerivatives(track, dydx);
 
   89        return QuickAdvance(track, dydx, hstep, dchord_step, dyerr);
 
   92    G4bool succeeded = false;
 
   96    G4double y[G4FieldTrack::ncompSVEC], dydx[G4FieldTrack::ncompSVEC];
 
  100    // hstep somtimes is too small. No need to add large curveLength.
 
  101    G4double curveLength = 0.0;
 
  102    G4double endCurveLength = hstep;
 
  106    if (hinitial > CLHEP::perMillion * hstep && hinitial < hstep)
 
  111    Base::GetStepper()->RightHandSide(y, dydx);
 
  113    for (G4int iter = 0; iter < Base::GetMaxNoSteps(); ++iter)
 
  115        const G4ThreeVector StartPos =
 
  116            field_utils::makeVector(y, field_utils::Value3D::Position);
 
  118        OneGoodStep(y, dydx, curveLength, h, eps, hdid, hnext);
 
  120        const G4ThreeVector EndPos =
 
  121            field_utils::makeVector(y, field_utils::Value3D::Position);
 
  123        CheckStep(EndPos, StartPos, hdid);
 
  125        G4double restCurveLength = endCurveLength - curveLength;
 
  126        if (restCurveLength < GetSmallestFraction() * hstep)
 
  131        h = std::min(hnext, restCurveLength);
 
  137        track.LoadFromArray(y, Base::GetStepper()->GetNumberOfVariables());
 
  138        track.SetCurveLength(track.GetCurveLength() + curveLength);
 
  144// Driver for one Runge-Kutta Step with monitoring of local truncation error
 
  145// to ensure accuracy and adjust stepsize. Input are dependent variable
 
  146// array y[0,...,5] and its derivative dydx[0,...,5] at the
 
  147// starting value of the independent variable x . Also input are stepsize
 
  148// to be attempted htry, and the required accuracy eps. On output y and x
 
  149// are replaced by their new values, hdid is the stepsize that was actually
 
  150// accomplished, and hnext is the estimated next stepsize.
 
  151// This is similar to the function rkqs from the book:
 
  152// Numerical Recipes in C: The Art of Scientific Computing (NRC), Second
 
  153// Edition, by William H. Press, Saul A. Teukolsky, William T.
 
  154// Vetterling, and Brian P. Flannery (Cambridge University Press 1992),
 
  155// 16.2 Adaptive StepSize Control for Runge-Kutta, p. 719
 
  158void G4FSALIntegrationDriver<T>::
 
  159OneGoodStep(G4double y[], 
 
  161            G4double& curveLength,   // InOut
 
  163            G4double eps_rel_max,
 
  164            G4double& hdid,         // Out
 
  165            G4double& hnext)        // Out
 
  167    G4double error2 = DBL_MAX;
 
  169    G4double yError[G4FieldTrack::ncompSVEC],
 
  170             yOut[G4FieldTrack::ncompSVEC],
 
  171             dydxOut[G4FieldTrack::ncompSVEC];
 
  173    // Set stepsize to the initial trial value
 
  174    G4double hstep = htry;
 
  176    static G4ThreadLocal G4int tot_no_trials = 0;
 
  177    const G4int max_trials = 100;
 
  179    for (G4int iter = 0; iter < max_trials; ++iter)
 
  183        Base::GetStepper()->Stepper(y, dydx, hstep, yOut, yError, dydxOut);
 
  184        error2 = field_utils::relativeError2(y, yError, hstep, eps_rel_max);
 
  187        if (error2 <= 1)  break;
 
  189        hstep = Base::ShrinkStepSize2(hstep, error2);
 
  192    hnext = Base::GrowStepSize2(hstep, error2);
 
  193    curveLength += (hdid = hstep);
 
  195    for(G4int k = 0; k < Base::GetStepper()->GetNumberOfVariables(); ++k)
 
  198        dydx[k] = dydxOut[k];
 
  203G4bool G4FSALIntegrationDriver<T>::
 
  204QuickAdvance( G4FieldTrack& fieldTrack, const G4double dydxIn[],
 
  206              G4double& dchord_step, G4double& dyerr )
 
  208    ++fNoQuickAvanceCalls;
 
  212        std::ostringstream message;
 
  213        message << "Proposed step is zero; hstep = " << hstep << " !";
 
  214        G4Exception("G4FSALIntegrationDriver ::QuickAdvance()",
 
  215                  "GeomField1001", JustWarning, message);
 
  220        std::ostringstream message;
 
  221        message << "Invalid run condition." << G4endl
 
  222                << "Proposed step is negative; hstep = "
 
  223                << hstep << "." << G4endl
 
  224                << "Requested step cannot be negative! Aborting event.";
 
  225        G4Exception("G4FSALIntegrationDriver ::QuickAdvance()",
 
  226                    "GeomField0003", EventMustBeAborted, message);
 
  230    G4double yError[G4FieldTrack::ncompSVEC],
 
  231             yIn[G4FieldTrack::ncompSVEC],
 
  232             yOut[G4FieldTrack::ncompSVEC],
 
  233             dydxOut[G4FieldTrack::ncompSVEC];
 
  235    fieldTrack.DumpToArray(yIn);
 
  237    Base::GetStepper()->Stepper(yIn, dydxIn, hstep, yOut, yError, dydxOut);
 
  238    dchord_step = Base::GetStepper()->DistChord();
 
  240    fieldTrack.LoadFromArray(yOut, Base::GetStepper()->GetNumberOfVariables());
 
  241    fieldTrack.SetCurveLength(fieldTrack.GetCurveLength() + hstep);
 
  243    dyerr = field_utils::absoluteError(yOut, yError, hstep);
 
  249void G4FSALIntegrationDriver<T>::SetSmallestFraction(G4double newFraction)
 
  251    if( newFraction > 1.e-16 && newFraction < 1e-8 )
 
  253        fSmallestFraction = newFraction;
 
  257        std::ostringstream message;
 
  258        message << "Smallest Fraction not changed. " << G4endl
 
  259                << "  Proposed value was " << newFraction << G4endl
 
  260                << "  Value must be between 1.e-8 and 1.e-16";
 
  261        G4Exception("G4FSALIntegrationDriver::SetSmallestFraction()",
 
  262                    "GeomField1001", JustWarning, message);
 
  267void G4FSALIntegrationDriver<T>::CheckStep(
 
  268    const G4ThreeVector& posIn, const G4ThreeVector& posOut, G4double hdid)
 
  270    const G4double endPointDist = (posOut - posIn).mag();
 
  271    if (endPointDist >= hdid * (1. + CLHEP::perMillion))
 
  273        ++fNoAccurateAdvanceBadSteps;
 
  275        // Issue a warning only for gross differences -
 
  276        // we understand how small difference occur.
 
  277        if (endPointDist >= hdid * (1. + perThousand))
 
  279           G4Exception("G4FSALIntegrationDriver::CheckStep()",
 
  280                       "GeomField1002", JustWarning,
 
  281                       "endPointDist >= hdid!");
 
  287       ++fNoAccurateAdvanceGoodSteps;
 
  292inline G4double G4FSALIntegrationDriver<T>::GetMinimumStep() const
 
  298void G4FSALIntegrationDriver<T>::SetMinimumStep(G4double minimumStepLength)
 
  300    fMinimumStep = minimumStepLength;
 
  304G4int G4FSALIntegrationDriver<T>::GetVerboseLevel() const
 
  306    return fVerboseLevel;
 
  310void G4FSALIntegrationDriver<T>::SetVerboseLevel(G4int newLevel)
 
  312    fVerboseLevel = newLevel;
 
  316G4double G4FSALIntegrationDriver<T>::GetSmallestFraction() const
 
  318    return fSmallestFraction;
 
  322void G4FSALIntegrationDriver<T>::IncrementQuickAdvanceCalls()
 
  324    ++fNoQuickAvanceCalls;
 
  329G4FSALIntegrationDriver<T>::AdvanceChordLimited(G4FieldTrack& track,
 
  332                                             G4double chordDistance)
 
  334   return ChordFinderDelegate::AdvanceChordLimitedImpl(track, hstep,
 
  339void G4FSALIntegrationDriver<T>::StreamInfo( std::ostream& os ) const
 
  341// Write out the parameters / state of the driver
 
  342  os << "State of G4IntegrationDriver: " << std::endl;
 
  343  os << "--Base state (G4RKIntegrationDriver): " << std::endl;
 
  344  Base::StreamInfo( os );
 
  345  os << "--Own  state (G4IntegrationDriver<>): " << std::endl;  
 
  346  os << "    fMinimumStep =      " << fMinimumStep  << std::endl;
 
  347  os << "    Smallest Fraction = " << fSmallestFraction << std::endl;
 
  349  os << "    verbose level     = " << fVerboseLevel << std::endl;    
 
  350  os << "    Reintegrates      = " << DoesReIntegrate() << std::endl;
 
  351  os << "--Chord Finder Delegate state: " << std::endl;
 
  352  ChordFinderDelegate::StreamDelegateInfo( os );