source: trunk/source/geometry/magneticfield/src/G4ChordFinder.cc @ 843

Last change on this file since 843 was 831, checked in by garnier, 16 years ago

import all except CVS

File size: 19.1 KB
Line 
1//
2// ********************************************************************
3// * License and Disclaimer                                           *
4// *                                                                  *
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.                             *
10// *                                                                  *
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.         *
17// *                                                                  *
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// ********************************************************************
25//
26//
27// $Id: G4ChordFinder.cc,v 1.47 2006/06/29 18:23:32 gunter Exp $
28// GEANT4 tag $Name:  $
29//
30//
31// 25.02.97 John Apostolakis,  design and implimentation
32// 05.03.97 V. Grichine , style modification
33// -------------------------------------------------------------------
34
35#include "G4ChordFinder.hh"
36#include "G4MagneticField.hh"
37#include "G4Mag_UsualEqRhs.hh"
38#include "G4ClassicalRK4.hh"
39
40#include <iomanip>
41
42// ..........................................................................
43
44G4ChordFinder::G4ChordFinder(G4MagInt_Driver* pIntegrationDriver)
45  : fDefaultDeltaChord( 0.25 * mm ), 
46    fDeltaChord( fDefaultDeltaChord ),
47    fAllocatedStepper(false),
48    fEquation(0), 
49    fDriversStepper(0), 
50    fFirstFraction(0.999), fFractionLast(1.00),  fFractionNextEstimate(0.98), 
51    fMultipleRadius(15.0), 
52    fTotalNoTrials_FNC(0), fNoCalls_FNC(0), fmaxTrials_FNC(0),
53    fStatsVerbose(0)
54{
55  // Simple constructor which does not create equation, ..
56      // fDeltaChord= fDefaultDeltaChord;
57  fIntgrDriver= pIntegrationDriver;
58  fAllocatedStepper= false;
59  fLastStepEstimate_Unconstrained = DBL_MAX;          // Should move q, p to
60
61  SetFractions_Last_Next( fFractionLast, fFractionNextEstimate); 
62    // check the values and set the other parameters
63}
64
65// ..........................................................................
66
67G4ChordFinder::G4ChordFinder( G4MagneticField*        theMagField,
68                              G4double                stepMinimum, 
69                              G4MagIntegratorStepper* pItsStepper )
70  : fDefaultDeltaChord( 0.25 * mm ), 
71    fDeltaChord( fDefaultDeltaChord ),
72    fAllocatedStepper(false),
73    fEquation(0), 
74    fDriversStepper(0), 
75    fFirstFraction(0.999), fFractionLast(1.00),  fFractionNextEstimate(0.98), 
76    fMultipleRadius(15.0), 
77    fTotalNoTrials_FNC(0), fNoCalls_FNC(0), fmaxTrials_FNC(0),
78    fStatsVerbose(0)
79{
80  //  Construct the Chord Finder
81  //  by creating in inverse order the  Driver, the Stepper and EqRhs ...
82  G4Mag_EqRhs *pEquation = new G4Mag_UsualEqRhs(theMagField);
83  fEquation = pEquation;                           
84  fLastStepEstimate_Unconstrained = DBL_MAX;          // Should move q, p to
85                                                     //    G4FieldTrack ??
86
87  SetFractions_Last_Next( fFractionLast, fFractionNextEstimate); 
88    // check the values and set the other parameters
89
90  // --->>  Charge    Q = 0
91  // --->>  Momentum  P = 1       NOMINAL VALUES !!!!!!!!!!!!!!!!!!
92
93  if( pItsStepper == 0 )
94  { 
95     pItsStepper = fDriversStepper = new G4ClassicalRK4(pEquation);
96     fAllocatedStepper= true;
97  }
98  else
99  {
100     fAllocatedStepper= false; 
101  }
102  fIntgrDriver = new G4MagInt_Driver(stepMinimum, pItsStepper, 
103                                     pItsStepper->GetNumberOfVariables() );
104}
105
106// ......................................................................
107
108void   
109G4ChordFinder::SetFractions_Last_Next( G4double fractLast, G4double fractNext )
110{ 
111  // Use -1.0 as request for Default.
112  if( fractLast == -1.0 )   fractLast = 1.0;   // 0.9;
113  if( fractNext == -1.0 )   fractNext = 0.98;  // 0.9;
114
115  // fFirstFraction  = 0.999;  // Orig 0.999 A safe value,  range: ~ 0.95 - 0.999
116  // fMultipleRadius = 15.0;   // For later use, range: ~  2 - 20
117
118  if( fStatsVerbose ) { 
119    G4cout << " ChordFnd> Trying to set fractions: "
120           << " first " << fFirstFraction
121           << " last " <<  fractLast
122           << " next " <<  fractNext
123           << " and multiple " << fMultipleRadius
124           << G4endl;
125  } 
126
127  if( (fractLast > 0.0) && (fractLast <=1.0) ) 
128    { fFractionLast= fractLast; }
129  else
130    G4cerr << "G4ChordFinder:: SetFractions_Last_Next: Invalid "
131           << " fraction Last = " << fractLast
132           << " must be  0 <  fractionLast <= 1 " << G4endl;
133  if( (fractNext > 0.0) && (fractNext <1.0) )
134    { fFractionNextEstimate = fractNext; }
135  else
136    G4cerr << "G4ChordFinder:: SetFractions_Last_Next: Invalid "
137           << " fraction Next = " << fractNext
138           << " must be  0 <  fractionNext < 1 " << G4endl;
139}
140
141// ......................................................................
142
143G4ChordFinder::~G4ChordFinder()
144{
145  delete   fEquation; // fIntgrDriver->pIntStepper->theEquation_Rhs;
146  if( fAllocatedStepper)
147  { 
148     delete fDriversStepper; 
149  }                                //  fIntgrDriver->pIntStepper;}
150  delete   fIntgrDriver; 
151
152  if( fStatsVerbose ) { PrintStatistics();  }
153}
154
155void
156G4ChordFinder::PrintStatistics()
157{
158  // Print Statistics
159  G4cout << "G4ChordFinder statistics report: " << G4endl;
160  G4cout
161    << "  No trials: " << fTotalNoTrials_FNC
162    << "  No Calls: "  << fNoCalls_FNC
163    << "  Max-trial: " <<  fmaxTrials_FNC
164    << G4endl; 
165  G4cout
166    << "  Parameters: " 
167    << "  fFirstFraction "  << fFirstFraction
168    << "  fFractionLast "   << fFractionLast
169    << "  fFractionNextEstimate " << fFractionNextEstimate
170    << G4endl; 
171}
172
173// ......................................................................
174
175G4double
176G4ChordFinder::AdvanceChordLimited( G4FieldTrack& yCurrent,
177                                    G4double      stepMax,
178                                    G4double      epsStep,
179                                    const G4ThreeVector latestSafetyOrigin,
180                                    G4double       latestSafetyRadius
181                                    )
182{
183  G4double stepPossible;
184  G4double dyErr;
185  G4FieldTrack yEnd( yCurrent);
186  G4double  startCurveLen= yCurrent.GetCurveLength();
187
188  G4double nextStep;
189  //            *************
190  stepPossible= FindNextChord(yCurrent, stepMax, yEnd, dyErr, epsStep, &nextStep
191                              , latestSafetyOrigin, latestSafetyRadius
192                             );
193  //            *************
194  G4bool good_advance;
195  if ( dyErr < epsStep * stepPossible )
196  {
197     // Accept this accuracy.
198     yCurrent = yEnd;
199     good_advance = true; 
200  }
201  else
202  {
203     // Advance more accurately to "end of chord"
204     //                           ***************
205     good_advance = fIntgrDriver->AccurateAdvance(yCurrent, stepPossible, epsStep, nextStep);
206     //                           ***************
207     if ( ! good_advance ){ 
208       // In this case the driver could not do the full distance
209       stepPossible= yCurrent.GetCurveLength()-startCurveLen;
210     }
211  }
212
213#ifdef G4DEBUG_FIELD
214  G4cout << "Exiting FindNextChord Limited with:" << G4endl
215         << "   yCurrent: " << yCurrent<< G4endl; 
216#endif
217
218  return stepPossible;
219}
220
221// #define  TEST_CHORD_PRINT  1
222
223// ............................................................................
224
225G4double
226G4ChordFinder::FindNextChord( const  G4FieldTrack  yStart,
227                                     G4double     stepMax,
228                                     G4FieldTrack&   yEnd, // Endpoint
229                                     G4double&   dyErrPos, // Error of endpoint
230                                     G4double    epsStep,
231                                     G4double*  pStepForAccuracy, 
232                              const  G4ThreeVector, //  latestSafetyOrigin,
233                                     G4double       //  latestSafetyRadius
234                                        )
235  // Returns Length of Step taken
236{
237  // G4int       stepRKnumber=0;
238  G4FieldTrack yCurrent=  yStart; 
239  G4double    stepTrial, stepForAccuracy;
240  G4double    dydx[G4FieldTrack::ncompSVEC]; 
241
242  //  1.)  Try to "leap" to end of interval
243  //  2.)  Evaluate if resulting chord gives d_chord that is good enough.
244  //     2a.)  If d_chord is not good enough, find one that is.
245 
246  G4bool    validEndPoint= false;
247  G4double  dChordStep, lastStepLength; //  stepOfLastGoodChord;
248
249  fIntgrDriver-> GetDerivatives( yCurrent, dydx )  ;
250
251  G4int     noTrials=0;
252  const G4double safetyFactor= fFirstFraction; //  0.975 or 0.99 ? was 0.999
253
254  stepTrial = std::min( stepMax, 
255                          safetyFactor * fLastStepEstimate_Unconstrained );
256
257  G4double newStepEst_Uncons= 0.0; 
258  do
259  { 
260     G4double stepForChord; 
261     yCurrent = yStart;    // Always start from initial point
262
263     //            ************
264     fIntgrDriver->QuickAdvance( yCurrent, dydx, stepTrial, 
265                                 dChordStep, dyErrPos);
266     //            ************
267
268     // We check whether the criterion is met here.
269     validEndPoint = AcceptableMissDist(dChordStep); 
270                      //  && (dyErrPos < eps) ;
271
272     lastStepLength = stepTrial; 
273
274     // This method estimates to step size for a good chord.
275     stepForChord = NewStep(stepTrial, dChordStep, newStepEst_Uncons );
276
277     if( ! validEndPoint ) {
278        if( stepTrial<=0.0 )
279          stepTrial = stepForChord; 
280        else if (stepForChord <= stepTrial) 
281          // Reduce by a fraction, possibly up to 20%
282          stepTrial = std::min( stepForChord, 
283                                fFractionLast * stepTrial); 
284        else
285          stepTrial *= 0.1;
286
287        // if(dbg) G4cerr<<"Dchord too big. Try new hstep="<<stepTrial<<G4endl;
288     }
289     // #ifdef  TEST_CHORD_PRINT
290     // TestChordPrint( noTrials, lastStepLength, dChordStep, stepTrial );
291     // #endif
292
293     noTrials++; 
294  }
295  while( ! validEndPoint );   // End of do-while  RKD
296
297  if( newStepEst_Uncons > 0.0  ){ 
298    fLastStepEstimate_Unconstrained= newStepEst_Uncons;
299  }
300
301  AccumulateStatistics( noTrials );
302
303  // stepOfLastGoodChord = stepTrial;
304
305  if( pStepForAccuracy ){ 
306     // Calculate the step size required for accuracy, if it is needed
307     G4double dyErr_relative = dyErrPos/(epsStep*lastStepLength);
308     if( dyErr_relative > 1.0 ) {
309        stepForAccuracy =
310           fIntgrDriver->ComputeNewStepSize( dyErr_relative,
311                                             lastStepLength );
312     }else{
313        stepForAccuracy = 0.0;   // Convention to show step was ok
314     }
315     *pStepForAccuracy = stepForAccuracy;
316  }
317
318#ifdef  TEST_CHORD_PRINT
319  static int dbg=0;
320  if( dbg ) 
321    G4cout << "ChordF/FindNextChord:  NoTrials= " << noTrials
322           << " StepForGoodChord=" << std::setw(10) << stepTrial << G4endl;
323#endif
324
325  yEnd=  yCurrent; 
326  return stepTrial; 
327}
328
329// ----------------------------------------------------------------------------
330#if 0         
331//   #ifdef G4VERBOSE
332if( dbg ) {
333   G4cerr << "Returned from QuickAdvance with: yCur=" << yCurrent <<G4endl;
334   G4cerr << " dChordStep= "<< dChordStep <<" dyErr=" << dyErr << G4endl;
335}
336#endif
337// ----------------------------------------------------------------------------
338
339// ...........................................................................
340
341G4double G4ChordFinder::NewStep(G4double  stepTrialOld, 
342                                G4double  dChordStep, // Curr. dchord achieved
343                                G4double& stepEstimate_Unconstrained ) 
344//
345// Is called to estimate the next step size, even for successful steps,
346// in order to predict an accurate 'chord-sensitive' first step
347// which is likely to assist in more performant 'stepping'.
348//
349       
350{
351  G4double stepTrial;
352  static G4double lastStepTrial = 1.,  lastDchordStep= 1.;
353
354#if 1
355  // const G4double  threshold = 1.21, multiplier = 0.9;
356  //  0.9 < 1 / std::sqrt(1.21)
357
358  if (dChordStep > 0.0)
359  {
360    stepEstimate_Unconstrained = stepTrialOld*std::sqrt( fDeltaChord / dChordStep );
361    // stepTrial =  0.98 * stepEstimate_Unconstrained;
362    stepTrial =  fFractionNextEstimate * stepEstimate_Unconstrained;
363  }
364  else
365  {
366    // Should not update the Unconstrained Step estimate: incorrect!
367    stepTrial =  stepTrialOld * 2.; 
368  }
369
370  // if ( dChordStep < threshold * fDeltaChord ){
371  //    stepTrial= stepTrialOld *  multiplier;   
372  // }
373  if( stepTrial <= 0.001 * stepTrialOld)
374  {
375     if ( dChordStep > 1000.0 * fDeltaChord ){
376        stepTrial= stepTrialOld * 0.03;   
377     }else{
378        if ( dChordStep > 100. * fDeltaChord ){
379          stepTrial= stepTrialOld * 0.1;   
380        }else{
381    // Try halving the length until dChordStep OK
382          stepTrial= stepTrialOld * 0.5;   
383        }
384     }
385  }else if (stepTrial > 1000.0 * stepTrialOld)
386  {
387     stepTrial= 1000.0 * stepTrialOld;
388  }
389
390  if( stepTrial == 0.0 ){
391     stepTrial= 0.000001;
392  }
393
394  lastStepTrial = stepTrialOld; 
395  lastDchordStep= dChordStep;
396#else
397  if ( dChordStep > 1000. * fDeltaChord ){
398        stepTrial= stepTrialOld * 0.03;   
399  }else{
400     if ( dChordStep > 100. * fDeltaChord ){
401        stepTrial= stepTrialOld * 0.1;   
402     }else{
403        // Keep halving the length until dChordStep OK
404        stepTrial= stepTrialOld * 0.5;   
405     }
406  }
407#endif
408
409  // A more sophisticated chord-finder could figure out a better
410  //   stepTrial, from dChordStep and the required d_geometry
411  //   eg
412  //      Calculate R, r_helix (eg at orig point)
413  //      if( stepTrial < 2 pi  R )
414  //          stepTrial = R arc_cos( 1 - fDeltaChord / r_helix )
415  //      else   
416  //          ??
417
418  return stepTrial;
419}
420
421//
422//  Given a starting curve point A (CurveA_PointVelocity),  a later
423//  curve point B (CurveB_PointVelocity) and a point E which is (generally)
424//  not on the curve, find and return a point F which is on the curve and
425//  which is close to E. While advancing towards F utilise eps_step
426//  as a measure of the relative accuracy of each Step.
427 
428G4FieldTrack
429G4ChordFinder::ApproxCurvePointV( const G4FieldTrack& CurveA_PointVelocity, 
430                                  const G4FieldTrack& CurveB_PointVelocity, 
431                                  const G4ThreeVector& CurrentE_Point,
432                                        G4double eps_step)
433{
434  // 1st implementation:
435  //    if r=|AE|/|AB|, and s=true path lenght (AB)
436  //    return the point that is r*s along the curve!
437
438  G4FieldTrack    Current_PointVelocity= CurveA_PointVelocity; 
439
440  G4ThreeVector  CurveA_Point= CurveA_PointVelocity.GetPosition();
441  G4ThreeVector  CurveB_Point= CurveB_PointVelocity.GetPosition();
442
443  G4ThreeVector  ChordAB_Vector= CurveB_Point   - CurveA_Point;
444  G4ThreeVector  ChordAE_Vector= CurrentE_Point - CurveA_Point;
445
446  G4double       ABdist= ChordAB_Vector.mag();
447  G4double  curve_length;  //  A curve length  of AB
448  G4double  AE_fraction; 
449 
450  curve_length= CurveB_PointVelocity.GetCurveLength()
451              - CurveA_PointVelocity.GetCurveLength(); 
452
453  // const
454  G4double  integrationInaccuracyLimit= std::max( perMillion, 0.5*eps_step ); 
455  if( curve_length < ABdist * (1. - integrationInaccuracyLimit) ){ 
456#ifdef G4DEBUG_FIELD
457    G4cerr << " Warning in G4ChordFinder::ApproxCurvePoint: "
458           << G4endl
459           << " The two points are further apart than the curve length "
460           << G4endl
461           << " Dist = "         << ABdist
462           << " curve length = " << curve_length
463           << " relativeDiff = " << (curve_length-ABdist)/ABdist
464           << G4endl;
465    if( curve_length < ABdist * (1. - 10*eps_step) ) {
466      G4cerr << " ERROR: the size of the above difference"
467             << " exceeds allowed limits.  Aborting." << G4endl;
468      G4Exception("G4ChordFinder::ApproxCurvePointV()", "PrecisionError",
469                  FatalException, "Unphysical curve length.");
470    }
471#endif
472    // Take default corrective action:
473    //    -->  adjust the maximum curve length.
474    //  NOTE: this case only happens for relatively straight paths.
475    curve_length = ABdist; 
476  }
477
478  G4double  new_st_length; 
479
480  if ( ABdist > 0.0 ){
481     AE_fraction = ChordAE_Vector.mag() / ABdist;
482  }else{
483     AE_fraction = 0.5;                         // Guess .. ?;
484#ifdef G4DEBUG_FIELD
485     G4cout << "Warning in G4ChordFinder::ApproxCurvePoint:"
486            << " A and B are the same point!" << G4endl
487            << " Chord AB length = " << ChordAE_Vector.mag() << G4endl
488            << G4endl;
489#endif
490  }
491 
492  if( (AE_fraction> 1.0 + perMillion) || (AE_fraction< 0.) ){
493#ifdef G4DEBUG_FIELD
494    G4cerr << " G4ChordFinder::ApproxCurvePointV - Warning:"
495           << " Anomalous condition:AE > AB or AE/AB <= 0 " << G4endl
496           << "   AE_fraction = " <<  AE_fraction << G4endl
497           << "   Chord AE length = " << ChordAE_Vector.mag() << G4endl
498           << "   Chord AB length = " << ABdist << G4endl << G4endl;
499    G4cerr << " OK if this condition occurs after a recalculation of 'B'"
500           << G4endl << " Otherwise it is an error. " << G4endl ; 
501#endif
502     // This course can now result if B has been re-evaluated,
503     //   without E being recomputed   (1 July 99)
504     //  In this case this is not a "real error" - but it undesired
505     //   and we cope with it by a default corrective action ...
506     AE_fraction = 0.5;                         // Default value
507  }
508
509  new_st_length= AE_fraction * curve_length; 
510
511  G4bool good_advance;
512  if ( AE_fraction > 0.0 ) { 
513     good_advance = 
514      fIntgrDriver->AccurateAdvance(Current_PointVelocity, 
515                                    new_st_length,
516                                    eps_step ); // Relative accuracy
517     // In this case it does not matter if it cannot advance the full distance
518  }
519
520  // If there was a memory of the step_length actually require at the start
521  // of the integration Step, this could be re-used ...
522
523  return Current_PointVelocity;
524}
525
526void
527G4ChordFinder::TestChordPrint( G4int    noTrials, 
528                               G4int    lastStepTrial, 
529                               G4double dChordStep, 
530                               G4double nextStepTrial )
531{
532     G4int oldprec= G4cout.precision(5);
533     G4cout << " ChF/fnc: notrial " << std::setw( 3) << noTrials
534            << " this_step= "       << std::setw(10) << lastStepTrial;
535     if( std::fabs( (dChordStep / fDeltaChord) - 1.0 ) < 0.001 ){
536             G4cout.precision(8);
537     }else{  G4cout.precision(6); }
538     G4cout << " dChordStep=  "     << std::setw(12) << dChordStep;
539     if( dChordStep > fDeltaChord ) { G4cout << " d+"; }
540     else                           { G4cout << " d-"; }
541     G4cout.precision(5);
542     G4cout <<  " new_step= "       << std::setw(10)
543            << fLastStepEstimate_Unconstrained
544            << " new_step_constr= " << std::setw(10)
545            << lastStepTrial << G4endl;
546     G4cout << " nextStepTrial = " << std::setw(10) << nextStepTrial << G4endl;
547     G4cout.precision(oldprec);
548}
Note: See TracBrowser for help on using the repository browser.