505 lines
15 KiB
Plaintext
505 lines
15 KiB
Plaintext
//
|
|
// ********************************************************************
|
|
// * 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. *
|
|
// ********************************************************************
|
|
//
|
|
//
|
|
// $Id: G4FSALIntegrationDriver.icc 110753 2018-06-12 15:44:03Z gcosmo $
|
|
//
|
|
//
|
|
// class G4FSALIntegrationDriver
|
|
//
|
|
// Class description:
|
|
//
|
|
// Driver class which controls the integration error of a
|
|
// Runge-Kutta stepper with a FSAL property
|
|
|
|
// History:
|
|
// - Created. D.Sorokin
|
|
// --------------------------------------------------------------------
|
|
|
|
#include "globals.hh"
|
|
#include "G4GeometryTolerance.hh"
|
|
#include "G4FieldTrack.hh"
|
|
#include "G4FieldUtils.hh"
|
|
|
|
#include <cassert>
|
|
|
|
|
|
template <class T>
|
|
G4FSALIntegrationDriver<T>::
|
|
G4FSALIntegrationDriver ( G4double hminimum, T* pStepper,
|
|
G4int numComponents, G4int statisticsVerbose )
|
|
: fSmallestFraction(1e-12),
|
|
fNoTotalSteps(0),
|
|
fNoBadSteps(0),
|
|
fNoGoodSteps(0),
|
|
fVerboseLevel(statisticsVerbose),
|
|
fNoQuickAvanceCalls(0)
|
|
{
|
|
if (numComponents != pStepper->GetNumberOfVariables())
|
|
{
|
|
std::ostringstream message;
|
|
message << "Driver's number of integrated components "
|
|
<< numComponents
|
|
<< " != Stepper's number of components "
|
|
<< pStepper->GetNumberOfVariables();
|
|
G4Exception("G4FSALIntegrationDriver","GeomField0002",
|
|
FatalException, message);
|
|
}
|
|
RenewStepperAndAdjust(pStepper);
|
|
fMinimumStep = hminimum;
|
|
fMaxStepBase = 250;
|
|
fMaxNoSteps = fMaxStepBase / pIntStepper->IntegratorOrder();
|
|
}
|
|
|
|
template <class T>
|
|
G4FSALIntegrationDriver<T>::~G4FSALIntegrationDriver()
|
|
{
|
|
#ifdef G4VERBOSE
|
|
if( fVerboseLevel > 0 )
|
|
G4cout << "G4FSALIntegration Driver Stats: "
|
|
<< "#QuickAdvance " << fNoQuickAvanceCalls
|
|
<< " - #AccurateAdvance " << fNoTotalSteps << G4endl;
|
|
#endif
|
|
}
|
|
|
|
// Runge-Kutta driver with adaptive stepsize control. Integrate starting
|
|
// values at y_current over hstep x2 with accuracy eps.
|
|
// On output ystart is replaced by values at the end of the integration
|
|
// interval. RightHandSide is the right-hand side of ODE system.
|
|
// The source is similar to odeint routine from NRC p.721-722 .
|
|
template <class T>
|
|
G4bool G4FSALIntegrationDriver<T>::
|
|
AccurateAdvance( G4FieldTrack& track, G4double hstep,
|
|
G4double eps, G4double hinitial )
|
|
{
|
|
if (hstep < GetMinimumStep())
|
|
{
|
|
G4double dchord_step = 0, dyerr = 0;
|
|
G4double dydx[G4FieldTrack::ncompSVEC];
|
|
GetDerivatives(track, dydx);
|
|
return QuickAdvance(track, dydx, hstep, dchord_step, dyerr);
|
|
}
|
|
|
|
G4bool succeeded = false;
|
|
|
|
G4double hnext, hdid;
|
|
|
|
|
|
G4double y[G4FieldTrack::ncompSVEC], dydx[G4FieldTrack::ncompSVEC];
|
|
|
|
track.DumpToArray(y);
|
|
|
|
// hstep somtimes is too small. No need to add large curveLength.
|
|
G4double curveLength = 0;
|
|
G4double endCurveLength = hstep;
|
|
|
|
|
|
G4double h = hstep;
|
|
if (hinitial > perMillion * hstep && hinitial < hstep)
|
|
{
|
|
h = hinitial;
|
|
}
|
|
|
|
pIntStepper->RightHandSide(y, dydx);
|
|
|
|
for (G4int iter = 0; iter < fMaxNoSteps; ++iter)
|
|
{
|
|
const G4ThreeVector StartPos =
|
|
field_utils::makeVector(y, field_utils::Value3D::Position);
|
|
|
|
OneGoodStep(y, dydx, curveLength, h, eps, hdid, hnext);
|
|
|
|
const G4ThreeVector EndPos =
|
|
field_utils::makeVector(y, field_utils::Value3D::Position);
|
|
|
|
CheckStep(EndPos, StartPos, hdid);
|
|
|
|
G4double restCurveLength = endCurveLength - curveLength;
|
|
if (restCurveLength < GetSmallestFraction() * hstep)
|
|
{
|
|
succeeded = true;
|
|
break;
|
|
}
|
|
h = std::min(hnext, restCurveLength);
|
|
}
|
|
|
|
|
|
if (succeeded)
|
|
{
|
|
track.LoadFromArray(y, pIntStepper->GetNumberOfVariables());
|
|
track.SetCurveLength(track.GetCurveLength() + curveLength);
|
|
}
|
|
|
|
return succeeded;
|
|
}
|
|
|
|
// Step failed; compute the size of retrial Step.
|
|
template <class T>
|
|
G4double G4FSALIntegrationDriver<T>::
|
|
ShrinkStepSize(G4double h, G4double error) const
|
|
{
|
|
if (error > errorConstraintShrink)
|
|
{
|
|
return max_stepping_decrease * h;
|
|
}
|
|
return GetSafety() * h * std::pow(error, GetPshrnk());
|
|
}
|
|
|
|
// Compute size of next Step
|
|
template<class T>
|
|
G4double G4FSALIntegrationDriver<T>::
|
|
GrowStepSize(G4double h, G4double error) const
|
|
{
|
|
if (error < errorConstraintGrow) {
|
|
return max_stepping_increase * h;
|
|
}
|
|
return GetSafety() * h * std::pow(error, GetPgrow());
|
|
}
|
|
|
|
// Driver for one Runge-Kutta Step with monitoring of local truncation error
|
|
// to ensure accuracy and adjust stepsize. Input are dependent variable
|
|
// array y[0,...,5] and its derivative dydx[0,...,5] at the
|
|
// starting value of the independent variable x . Also input are stepsize
|
|
// to be attempted htry, and the required accuracy eps. On output y and x
|
|
// are replaced by their new values, hdid is the stepsize that was actually
|
|
// accomplished, and hnext is the estimated next stepsize.
|
|
// This is similar to the function rkqs from the book:
|
|
// Numerical Recipes in C: The Art of Scientific Computing (NRC), Second
|
|
// Edition, by William H. Press, Saul A. Teukolsky, William T.
|
|
// Vetterling, and Brian P. Flannery (Cambridge University Press 1992),
|
|
// 16.2 Adaptive StepSize Control for Runge-Kutta, p. 719
|
|
//
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::
|
|
OneGoodStep( G4double y[], G4double dydx[],
|
|
G4double& curveLength, // InOut
|
|
G4double htry, G4double eps_rel_max,
|
|
G4double& hdid, // Out
|
|
G4double& hnext ) // Out
|
|
{
|
|
G4double error = DBL_MAX;
|
|
|
|
G4double yError[G4FieldTrack::ncompSVEC],
|
|
yOut[G4FieldTrack::ncompSVEC],
|
|
dydxOut[G4FieldTrack::ncompSVEC];
|
|
|
|
// Set stepsize to the initial trial value
|
|
G4double hstep = htry;
|
|
|
|
static G4ThreadLocal G4int tot_no_trials = 0;
|
|
const G4int max_trials = 100;
|
|
|
|
for (G4int iter = 0; iter < max_trials; ++iter)
|
|
{
|
|
++tot_no_trials;
|
|
|
|
pIntStepper->Stepper(y, dydx, hstep, yOut, yError, dydxOut);
|
|
error = field_utils::relativeError(y, yError, hstep, eps_rel_max);
|
|
|
|
// Step succeeded.
|
|
if (error <= 1) break;
|
|
|
|
hstep = ShrinkStepSize(hstep, error);
|
|
}
|
|
|
|
hnext = GrowStepSize(hstep, error);
|
|
curveLength += (hdid = hstep);
|
|
|
|
for(G4int k = 0; k < pIntStepper->GetNumberOfVariables(); ++k)
|
|
{
|
|
y[k] = yOut[k];
|
|
dydx[k] = dydxOut[k];
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
G4bool G4FSALIntegrationDriver<T>::
|
|
QuickAdvance( G4FieldTrack& fieldTrack, const G4double dydxIn[],
|
|
G4double hstep, G4double& dchord_step, G4double& dyerr )
|
|
{
|
|
++fNoQuickAvanceCalls;
|
|
|
|
if (hstep == 0)
|
|
{
|
|
std::ostringstream message;
|
|
message << "Proposed step is zero; hstep = " << hstep << " !";
|
|
G4Exception("G4FSALIntegrationDriver ::QuickAdvance()",
|
|
"GeomField1001", JustWarning, message);
|
|
return true;
|
|
}
|
|
if (hstep < 0)
|
|
{
|
|
std::ostringstream message;
|
|
message << "Invalid run condition." << G4endl
|
|
<< "Proposed step is negative; hstep = "
|
|
<< hstep << "." << G4endl
|
|
<< "Requested step cannot be negative! Aborting event.";
|
|
G4Exception("G4FSALIntegrationDriver ::QuickAdvance()",
|
|
"GeomField0003", EventMustBeAborted, message);
|
|
return false;
|
|
}
|
|
|
|
G4double yError[G4FieldTrack::ncompSVEC],
|
|
yIn[G4FieldTrack::ncompSVEC],
|
|
yOut[G4FieldTrack::ncompSVEC],
|
|
dydxOut[G4FieldTrack::ncompSVEC];
|
|
|
|
fieldTrack.DumpToArray(yIn);
|
|
|
|
pIntStepper->Stepper(yIn, dydxIn, hstep, yOut, yError, dydxOut);
|
|
dchord_step = pIntStepper->DistChord();
|
|
|
|
fieldTrack.LoadFromArray(yOut, pIntStepper->GetNumberOfVariables());
|
|
fieldTrack.SetCurveLength(fieldTrack.GetCurveLength() + hstep);
|
|
|
|
dyerr = field_utils::relativeError(yOut, yError, hstep) * hstep;
|
|
|
|
return true;
|
|
}
|
|
|
|
template <class T>
|
|
G4double G4FSALIntegrationDriver<T>::
|
|
ComputeNewStepSize( G4double errMaxNorm, // max error (normalised)
|
|
G4double hstepCurrent ) // current step size
|
|
{
|
|
if (errMaxNorm > 1)
|
|
{
|
|
return ShrinkStepSize(hstepCurrent, errMaxNorm);
|
|
}
|
|
else if(errMaxNorm >= 0)
|
|
{
|
|
return GrowStepSize(hstepCurrent, errMaxNorm);
|
|
}
|
|
|
|
G4Exception("G4FSALIntegrationDriver::ConputeNewStepSize", "GeomField0003",
|
|
FatalException, "Error is negative!");
|
|
|
|
return max_stepping_increase * hstepCurrent;
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::SetSmallestFraction(G4double newFraction)
|
|
{
|
|
if( newFraction > 1.e-16 && newFraction < 1e-8 )
|
|
{
|
|
fSmallestFraction = newFraction;
|
|
}
|
|
else
|
|
{
|
|
std::ostringstream message;
|
|
message << "Smallest Fraction not changed. " << G4endl
|
|
<< " Proposed value was " << newFraction << G4endl
|
|
<< " Value must be between 1.e-8 and 1.e-16";
|
|
G4Exception("G4FSALIntegrationDriver::SetSmallestFraction()",
|
|
"GeomField1001", JustWarning, message);
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::GetDerivatives(
|
|
const G4FieldTrack& track, G4double dydx[]) const
|
|
{
|
|
G4double y[G4FieldTrack::ncompSVEC];
|
|
track.DumpToArray(y);
|
|
pIntStepper->RightHandSide(y, dydx);
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::CheckStep(
|
|
const G4ThreeVector& posIn, const G4ThreeVector& posOut, G4double hdid)
|
|
{
|
|
++fNoTotalSteps;
|
|
|
|
const G4double endPointDist = (posOut - posIn).mag();
|
|
if (endPointDist >= hdid * (1. + perMillion))
|
|
{
|
|
++fNoBadSteps;
|
|
#ifdef G4DEBUG_FIELD
|
|
// Issue a warning only for gross differences -
|
|
// we understand how small difference occur.
|
|
if (endPointDist >= hdid * (1. + perThousand))
|
|
{
|
|
G4Exception("G4FSALIntegrationDriver::CheckStep()",
|
|
"GeomField1002", JustWarning,
|
|
"endPointDist >= hdid!");
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
++fNoGoodSteps;
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::UpdateErrorConstraints()
|
|
{
|
|
errorConstraintShrink = std::pow(
|
|
max_stepping_decrease / GetSafety(), 1. / GetPshrnk());
|
|
|
|
errorConstraintGrow = std::pow(
|
|
max_stepping_increase / GetSafety(), 1. / GetPgrow());
|
|
}
|
|
|
|
template <class T>
|
|
inline G4double G4FSALIntegrationDriver<T>::GetMinimumStep() const
|
|
{
|
|
return fMinimumStep;
|
|
}
|
|
|
|
template <class T>
|
|
inline G4double G4FSALIntegrationDriver<T>::GetSafety() const
|
|
{
|
|
return safety;
|
|
}
|
|
|
|
template <class T>
|
|
inline G4double G4FSALIntegrationDriver<T>::GetPshrnk() const
|
|
{
|
|
return pshrnk;
|
|
}
|
|
|
|
template <class T>
|
|
G4double G4FSALIntegrationDriver<T>::GetPgrow() const
|
|
{
|
|
return pgrow;
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::SetMinimumStep(G4double minimumStepLength)
|
|
{
|
|
fMinimumStep = minimumStepLength;
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::ReSetParameters(G4double new_safety)
|
|
{
|
|
safety = new_safety;
|
|
pshrnk = -1.0 / pIntStepper->IntegratorOrder();
|
|
pgrow = -1.0 / (1.0 + pIntStepper->IntegratorOrder());
|
|
UpdateErrorConstraints();
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::SetSafety(G4double val)
|
|
{
|
|
safety = val;
|
|
UpdateErrorConstraints();
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::
|
|
RenewStepperAndAdjust(G4MagIntegratorStepper* stepper)
|
|
{
|
|
T* ourStepper= dynamic_cast<T*>(stepper);
|
|
if ( ourStepper )
|
|
{
|
|
RenewStepperAndAdjustStrict( ourStepper );
|
|
}
|
|
else
|
|
{
|
|
G4Exception("G4FSALIntegrationDriver::RenewStepperAndAdjust()",
|
|
"GeomField0002", FatalException,
|
|
"The type of the stepper provided is incorrect for this templated driver");
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::RenewStepperAndAdjustStrict(T* stepper)
|
|
{
|
|
pIntStepper = stepper;
|
|
ReSetParameters();
|
|
}
|
|
|
|
template <class T>
|
|
const T* G4FSALIntegrationDriver<T>::GetStepperOfPreciseType() const
|
|
{
|
|
return pIntStepper;
|
|
}
|
|
|
|
template <class T>
|
|
T* G4FSALIntegrationDriver<T>::GetStepperOfPreciseType()
|
|
{
|
|
return pIntStepper;
|
|
}
|
|
|
|
template <class T>
|
|
const G4MagIntegratorStepper* G4FSALIntegrationDriver<T>::GetStepper() const
|
|
{
|
|
return nullptr; // pIntStepper;
|
|
// It can only return 'pIntStepper' if it is a compatible type
|
|
}
|
|
|
|
template <class T>
|
|
G4MagIntegratorStepper* G4FSALIntegrationDriver<T>::GetStepper()
|
|
{
|
|
return nullptr;
|
|
// It can only return 'pIntStepper' if it is a compatible type
|
|
}
|
|
|
|
|
|
template <class T>
|
|
G4int G4FSALIntegrationDriver<T>::GetMaxNoSteps() const
|
|
{
|
|
return fMaxNoSteps;
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::SetMaxNoSteps(G4int val)
|
|
{
|
|
fMaxNoSteps = val;
|
|
}
|
|
|
|
template <class T>
|
|
G4int G4FSALIntegrationDriver<T>::GetVerboseLevel() const
|
|
{
|
|
return fVerboseLevel;
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::SetVerboseLevel(G4int newLevel)
|
|
{
|
|
fVerboseLevel = newLevel;
|
|
}
|
|
|
|
template <class T>
|
|
G4double G4FSALIntegrationDriver<T>::GetSmallestFraction() const
|
|
{
|
|
return fSmallestFraction;
|
|
}
|
|
|
|
template <class T>
|
|
G4EquationOfMotion* G4FSALIntegrationDriver<T>::GetEquationOfMotion()
|
|
{
|
|
return pIntStepper->GetEquationOfMotion();
|
|
}
|
|
|
|
template <class T>
|
|
void G4FSALIntegrationDriver<T>::
|
|
SetEquationOfMotion(G4EquationOfMotion* equation)
|
|
{
|
|
pIntStepper->SetEquationOfMotion(equation);
|
|
}
|