source: trunk/source/geometry/solids/test/fred/src/FredTest3.cc @ 1316

Last change on this file since 1316 was 1316, checked in by garnier, 14 years ago

update geant4-09-04-beta-cand-01 interfaces-V09-03-09 vis-V09-03-08

File size: 19.3 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// FredTest3
28//
29// Implementation of fred's third geometry tester
30//
31#include "FredTest3.hh"
32
33#include "Randomize.hh"
34#include "G4VSolid.hh"
35#include "G4UImanager.hh"
36#include "G4GeometryTolerance.hh"
37
38#include <time.h>
39#include <iomanip>
40#include <sstream>
41
42//
43// Constructor
44//
45FredTest3::FredTest3()
46{
47        //
48        // Defaults
49        //
50        SetDefaults();
51       
52        //
53        // Zero error list
54        //
55        errorList = 0;
56}
57
58
59//
60// Destructor
61//
62FredTest3::~FredTest3()
63{
64        ClearErrors();
65}
66
67
68//
69// SetDefaults
70//
71// Set defaults values
72//
73void FredTest3::SetDefaults()
74{
75        target = G4ThreeVector( 0, 0, 0 );
76        widths = G4ThreeVector( 1*m, 1*m, 1*m );
77        grids  = G4ThreeVector( 0, 0, 0 );
78       
79        maxPoints = 10000;
80        maxErrors = 100;
81}
82
83
84//
85// GetRandomPoint
86//
87// Return a random point in three dimensions using the current
88// specs
89//
90G4ThreeVector FredTest3::GetRandomPoint() const {
91        G4double dx = widths.x()*GaussianRandom(10*m/widths.x()),
92                 dy = widths.y()*GaussianRandom(10*m/widths.y()),
93                 dz = widths.z()*GaussianRandom(10*m/widths.z());
94                 
95                 
96        if (grids.x() > 0) dx = grids.x()*G4int( dx/grids.x()+1 );
97        if (grids.y() > 0) dy = grids.y()*G4int( dx/grids.y()+1 );
98        if (grids.z() > 0) dz = grids.z()*G4int( dx/grids.z()+1 );
99
100        G4ThreeVector randvec( dx, dy, dz );
101       
102        return target + randvec;
103}
104
105
106//
107// GaussianRandom
108//
109// Return Gaussian random number of unit width
110//
111// A classic, slow, but remarkably effective algorithm. Certainly good
112// enough for our purposes.
113//
114G4double FredTest3::GaussianRandom(const G4double cutoff) const {
115        if (cutoff <= 0) G4Exception( "Illegal cutoff" );
116
117        G4double answer;
118        do {
119                answer = -3.0;
120                for( G4int j = 0; j < 6; j++ ) answer += G4UniformRand();
121                answer *= std::sqrt(2.0);
122        } while( std::fabs(answer) > cutoff );
123       
124        return(answer);
125}
126
127
128//
129// RunTest
130//
131// Do your stuff!
132//
133void FredTest3::RunTest( const G4VSolid *testVolume, std::ostream &logger )
134{
135        //
136        // Clear error list
137        //
138        ClearErrors();
139       
140        //
141        // Output test parameters
142        //
143        time_t now;
144        time(&now);
145        G4String dateTime(ctime(&now));
146       
147        logger << "% Fred test3 logged output " << dateTime;
148        logger << "% volume =    " << testVolume->GetName() << G4endl;
149        logger << "% target =    " << target << G4endl;
150        logger << "% widths =    " << widths << G4endl;
151        logger << "% grids  =    " << grids  << G4endl;
152        logger << "% maxPoints = " << maxPoints << G4endl;
153        logger << "% maxErrors = " << maxErrors << G4endl;
154
155        //
156        // Setup lists
157        //
158        FredTest3PointList inside(100);
159        FredTest3PointList outside(100);
160        FredTest3PointList surface(100);
161       
162        //
163        // Set iostream precision to 14 digits
164        //
165        logger << std::setprecision(14);
166       
167        //
168        // Set clock
169        //
170        // clock_t start = clock();
171       
172        //
173        // Loop over points
174        //
175        G4int nIn = 0, nOut = 0, nSurf = 0;
176       
177        G4int nPoint = 0;
178        G4int nError = 0;
179       
180        for(;;) {
181 
182                //
183                // Generate a random point
184                //
185                G4ThreeVector   point = GetRandomPoint();
186               
187                //
188                // Catagorize, test, and store
189                //
190                EInside catagory = testVolume->Inside( point );
191                switch( catagory ) {
192                        case kOutside:
193                        nOut++;
194                        TestOutsidePoint( testVolume, &nError, &inside, point, logger );
195                        outside.AddPoint( point );
196                        break;
197                       
198                        case kInside:
199                        nIn++;
200                        TestInsidePoint( testVolume, &nError, &outside, point, logger );
201                        inside.AddPoint( point );
202                        break;
203                       
204                        case kSurface:
205                        nSurf++;
206//                      TestSurfacePoint( testVolume, &nError, point, logger );
207                        surface.AddPoint( point );
208                        break;
209                }
210               
211                //
212                // Return early if we have accumulated enough errors
213                //
214                if (nError >= maxErrors) {
215                        logger << "% End of test (maximum number errors) ";
216                        break;
217                }
218                if (++nPoint >= maxPoints) {
219                        logger << "% End of test (maximum number points) ";
220                        break;
221                }
222       
223        }
224
225        time(&now);
226        G4String dateTime2(ctime(&now));
227        logger << dateTime2;
228
229        logger << "% Statistics: points=" << nPoint << " errors reported=" << nError << G4endl;
230
231        logger << "%             inside=" << nIn << " outside=" << nOut
232               << " surface=" << nSurf << G4endl;
233        logger << "%             cpu time=" << clock()/CLOCKS_PER_SEC << G4endl;
234               
235        logger << "%(End of file)" << G4endl;
236}
237
238
239//
240// DebugError
241//
242// Recover previously logged error and setup particle gun appropriately
243//
244void FredTest3::RunDebug( const G4VSolid* solid, std::istream &logger )
245{
246
247        G4UImanager *UI = G4UImanager::GetUIpointer();
248        G4int errorIndex = 0;
249        while ( ! DebugError(solid, logger, ++errorIndex) ) {
250
251                UI->ApplyCommand( "/run/beamOn 1" );
252        }       
253}
254
255//
256// DebugError
257//
258// Recover previously logged error and setup particle gun appropriately
259//
260G4int FredTest3::DebugError( const G4VSolid *, std::istream &logger,
261                             const G4int errorIndex ) const
262{
263        G4ThreeVector p, v;
264       
265        //
266        // Recover information from log file
267        //
268        G4int error = GetLoggedPV( logger, errorIndex, p, v );
269        if (error) return error;
270       
271        G4cout << "DebugError  " << errorIndex << ":  p=" << p << ",  v=" << v << G4endl; 
272                       
273        //
274        // Setup fred to simulate this event
275        //
276        // We do this using the command line, a cheesy short-cut but
277        // rather useful in hacks like this.
278        //
279        // If you are writing your own serious GEANT4 application,
280        // please do something better.
281        //
282        G4UImanager *UI = G4UImanager::GetUIpointer();
283
284        UI->ApplyCommand( "/tracking/verbose 1" );
285
286        UI->ApplyCommand( "/fred/gun G4" );
287       
288        UI->ApplyCommand( "/gun/particle geantino" );
289       
290        std::ostringstream formatter1;
291        formatter1 << std::setprecision(14);
292        formatter1 << "/gun/position "  << p.x() << " " << p.y() << " " << p.z() << " mm" << G4endl;
293        G4String commandBuffer=formatter1.str();
294        UI->ApplyCommand( commandBuffer );
295       
296        std::ostringstream formatter2;
297        formatter2 << std::setprecision(14);
298        formatter2 << "/gun/direction " << v.x() << " " << v.y() << " " << v.z() << " mm" << G4endl;
299        commandBuffer=formatter2.str();
300        UI->ApplyCommand( commandBuffer );
301        return 0;
302}
303
304
305//
306// DebugInside
307//
308// Recover previously logged error and invoke G4VSolid::Inside
309//
310G4int FredTest3::DebugInside( const G4VSolid *testVolume, std::istream &logger, const G4int errorIndex ) const
311{       
312        G4ThreeVector p, v;
313       
314        //
315        // Recover information from log file
316        //
317        G4int error = GetLoggedPV( logger, errorIndex, p, v );
318        if (error) return error;
319       
320        //
321        // Call
322        //
323        G4cout << "testVolume->Inside(p): " << testVolume->Inside( p ) << G4endl; 
324        return 0;
325}
326
327
328//
329// DebugToInP
330//
331// Recover previously logged error and invoke G4VSolid::DistanceToIn(p)
332//
333G4int FredTest3::DebugToInP( const G4VSolid *testVolume, std::istream &logger, const G4int errorIndex ) const
334{       
335        G4ThreeVector p, v;
336       
337        //
338        // Recover information from log file
339        //
340        G4int error = GetLoggedPV( logger, errorIndex, p, v );
341        if (error) return error;
342       
343        //
344        // Call
345        //
346        G4cout << "testVolume->DistanceToIn(p): " <<  testVolume->DistanceToIn( p )  << G4endl; 
347        return 0;
348}
349
350
351//
352// DebugToInPV
353//
354// Recover previously logged error and invoke G4VSolid::DistanceToIn(p,v)
355//
356G4int FredTest3::DebugToInPV( const G4VSolid *testVolume, std::istream &logger, const G4int errorIndex ) const
357{       
358        G4ThreeVector p, v;
359       
360        //
361        // Recover information from log file
362        //
363        G4int error = GetLoggedPV( logger, errorIndex, p, v );
364        if (error) return error;
365       
366        //
367        // Call
368        //
369        G4double answer = testVolume->DistanceToIn( p, v );
370        G4cout << "testVolume->DistanceToIn(p,v): " << answer << G4endl; 
371       
372        p += answer*v;
373       
374        G4cout << "testVolume->Inside(p+=answer*v):" <<  p << " " << testVolume->Inside(p) << G4endl;
375        return 0;
376}
377
378
379//
380// DebugToOutP
381//
382// Recover previously logged error and invoke G4VSolid::DistanceToOut(p)
383//
384G4int FredTest3::DebugToOutP( const G4VSolid *testVolume, std::istream &logger, const G4int errorIndex ) const
385{       
386        G4ThreeVector p, v;
387       
388        //
389        // Recover information from log file
390        //
391        G4int error = GetLoggedPV( logger, errorIndex, p, v );
392        if (error) return error;
393       
394        //
395        // Call
396        //
397        G4cout << "testVolume->DistanceToOut(p): " << testVolume->DistanceToOut( p ) << G4endl; 
398
399        return 0;
400}
401
402
403//
404// DebugToOutPV
405//
406// Recover previously logged error and invoke G4VSolid::DistanceToOut(p,v)
407//
408G4int FredTest3::DebugToOutPV( const G4VSolid *testVolume, std::istream &logger, const G4int errorIndex ) const
409{       
410        G4ThreeVector p, v;
411       
412        //
413        // Recover information from log file
414        //
415        G4int error = GetLoggedPV( logger, errorIndex, p, v );
416        if (error) return error;
417
418        //
419        // Call
420        //
421        G4bool validNorm;
422        G4ThreeVector norm;
423        G4double answer = testVolume->DistanceToOut( p, v, true, &validNorm, &norm);
424        G4cout << "testVolume->DistanceToOut( p, v ): " << answer << " validNorm: " << validNorm << G4endl; 
425       
426        p += answer*v;
427        G4cout << "testVolume->Inside(p += answer*v): " << testVolume->Inside(p) << G4endl; 
428       
429        return 0;
430}
431
432
433//
434// --------------------------------------
435// Tests
436//
437
438//
439// TestOutsidePoint
440//
441void FredTest3::TestOutsidePoint( const G4VSolid *testVolume, G4int *nError,
442                                  const FredTest3PointList *inside, const G4ThreeVector point, std::ostream &logger )
443{
444        // Check if point is really outside
445        G4double safeDistance = testVolume->DistanceToIn( point );
446        if (safeDistance <= 0.0) {
447                ReportError( nError, point, 0, "T0: DistanceToIn(p) <= 0", logger );
448                return;
449        }
450       
451        // Loop over inside points
452        for( G4int i=0; i < inside->NumPoints(); i++ ) {
453 
454                // Direction vector from tested point to the inside point
455                G4ThreeVector vr = (*inside)[i] - point;
456                G4ThreeVector v = vr.unit();
457               
458                // Distance from the outside point to solid in direction
459                // to the tested inside point
460                G4double dist = testVolume->DistanceToIn( point, v );
461
462                if (dist <= 0) {
463                        ReportError( nError, point, v, "T0: DistanceToIn(p,v) <= 0", logger );
464                        continue;
465                }
466                if (dist == kInfinity) {
467                        ReportError( nError, point, v, "T0: DistanceToIn(p,v) == kInfinity", logger );
468                        continue;
469                }
470                if (dist < safeDistance+1E-10) {
471                        ReportError( nError, point, v, "T0: DistanceToIn(p,v) < DistanceToIn(p)", logger );
472                        continue;
473                }
474               
475                // Tested point moved to the solid surface
476                // (in direction to the tested inside point):
477                G4ThreeVector p = point + dist*v;
478               
479                // Inside(p) should return kSurface.
480                // If kOutside is returned, report undershoots; if kInside, report overshoots
481                EInside insideOrNot = testVolume->Inside( p );
482                if (insideOrNot == kOutside) {
483                        ReportError( nError, point, v, "T0: DistanceToIn(p,v) undershoots", logger );
484                        continue;
485                }
486                if (insideOrNot == kInside) {
487                        ReportError( nError, point, v, "TO: DistanceToIn(p,v) overshoots", logger );
488                        continue;
489                }
490               
491                // DistanceToIn for the computed point on the surface
492                dist = testVolume->DistanceToIn( p );
493                //if (dist != 0) {
494                if (dist > G4GeometryTolerance::GetInstance()->GetSurfaceTolerance()) {
495                        ReportError( nError, p, v, "T02: DistanceToIn(p) should be zero", logger );
496                        continue;
497                }
498               
499                // DistanceToOut for the computed point on the surface
500                dist = testVolume->DistanceToOut( p );
501                //if (dist != 0) {
502                if (dist > G4GeometryTolerance::GetInstance()->GetSurfaceTolerance()) {
503                        ReportError( nError, p, v, "T02: DistanceToOut(p) should be zero", logger );
504                        continue;
505                }
506               
507                // DistanceToIn with direction for the computed point on the surface
508                dist = testVolume->DistanceToIn( p, v );
509                //if (dist != 0) {
510                if (dist > G4GeometryTolerance::GetInstance()->GetSurfaceTolerance()) {
511                        ReportError( nError, p, v, "T02: DistanceToIn(p,v) should be zero", logger );
512                        continue;
513                }       
514               
515                // DistanceToOut with direction for the computed point on the surface
516                G4bool validNorm;
517                G4ThreeVector norm;
518               
519                dist = testVolume->DistanceToOut( p, v, true, &validNorm, &norm );
520                // if (dist == 0) continue;
521                // if (dist < G4GeometryTolerance::GetInstance()->GetSurfaceTolerance()) continue;
522                // Why quit here withour Error ???
523   
524                if (dist == kInfinity) {
525                        ReportError( nError, p, v, "T02: DistanceToOut(p,v) == kInfinity", logger );
526                        continue;
527                }
528               
529                if (validNorm) {
530                        if (norm.dot(v) < 0) {
531                                ReportError( nError, p, v, "T02: Outgoing normal incorrect", logger );
532                                continue;
533                        }
534                }
535               
536                // The point on surface (entering point) moved to the second point
537                // on the surface (exiting point)
538                // (in direction to the tested inside point):
539                G4ThreeVector p2 = p + v*dist;
540               
541                insideOrNot = testVolume->Inside(p2);
542                if (insideOrNot == kInside) {
543                        ReportError( nError, p, v, "T02: DistanceToOut(p,v) undershoots", logger );
544                        continue;
545                }
546                if (insideOrNot == kOutside) {
547                        ReportError( nError, p, v, "TO2: DistanceToOut(p,v) overshoots", logger );
548                        continue;
549                }
550                       
551                // DistanceToIn from the exiting point (in the exiting direction)
552                // If infinity, there is no intersectin with solid anymore, what means that the solid
553                // lied entirely between entering and existing points, and the validNorm should have been true.
554               
555                dist = testVolume->DistanceToIn(p2,v);
556                if (validNorm) {
557                        if (dist != kInfinity) {
558                                ReportError( nError, p, v, "TO2: DistanceToOut incorrectly returns validNorm==true", logger );
559                                continue;
560                        }
561                }
562                else {
563                        if (dist == kInfinity) {
564                                ReportError( nError, p, v, "TO2: DistanceToOut incorrectly returns validNorm==false", logger );
565                                continue;
566                        }
567                }
568        }
569}
570
571
572
573//
574// TestInsidePoint
575//
576void FredTest3::TestInsidePoint( const G4VSolid *testVolume, G4int *nError,
577                                 const FredTest3PointList *outside, const G4ThreeVector point, std::ostream &logger )
578{
579        // Check if point is really inside
580        G4double safeDistance = testVolume->DistanceToOut( point );
581        if (safeDistance <= 0.0) {
582                ReportError( nError, point, 0, "TI: DistanceToOut(p) <= 0", logger );
583                return;
584        }
585       
586        // Loop over outside points
587        for( G4int i=0; i < outside->NumPoints(); i++ ) {
588
589                // Direction vector from tested point to the outside point
590                G4ThreeVector vr = (*outside)[i] - point;
591                G4ThreeVector v = vr.unit();
592
593                G4bool validNorm;
594                G4ThreeVector norm;
595               
596                // Distance from the inside point to solid border in direction
597                // to the tested outside point
598                G4double dist = testVolume->DistanceToOut( point, v, true, &validNorm, &norm );
599                if (dist <= 0) {
600                        ReportError( nError, point, v, "TI: DistanceToOut(p,v) <= 0", logger );
601                        continue;
602                }
603                if (dist == kInfinity) {
604                        ReportError( nError, point, v, "TI: DistanceToOut(p,v) == kInfinity", logger );
605                        continue;
606                }
607                if (dist < safeDistance+1E-10) {
608                        ReportError( nError, point, v, "TI: DistanceToOut(p,v) < DistanceToOut(p)", logger );
609                        continue;
610                }
611
612                if (validNorm) {
613                        if (norm.dot(v) < 0) {
614                                ReportError( nError, point, v, "TI: Outgoing normal incorrect", logger );
615                                continue;
616                        }
617                }
618               
619                // Tested point moved to the solid surface
620                // (in direction to the tested outside point):
621                G4ThreeVector p = point + v*dist;
622               
623                EInside insideOrNot = testVolume->Inside(p);
624                if (insideOrNot == kInside) {
625                        ReportError( nError, point, v, "TI: DistanceToOut(p,v) undershoots", logger );
626                        continue;
627                }
628                if (insideOrNot == kOutside) {
629                        ReportError( nError, point, v, "TI: DistanceToOut(p,v) overshoots", logger );
630                        continue;
631                }
632        }
633}
634
635
636//
637// ReportError
638//
639// Report the specified error message, but only if it has not been reported a zillion
640// times already.
641//
642void FredTest3::ReportError( G4int *nError, const G4ThreeVector p, 
643                             const G4ThreeVector v, const G4String comment, std::ostream &logger )
644{
645        //
646        // Have we encountered this error message before?
647        //
648        FredTest3ErrorList *last=0, *errors = errorList;
649        while( errors ) {
650                //
651                // Note: below is an expensive comparison. This could be replaced with something
652                // faster if we really wanted, like a hash. But is it worth the trouble? Nah.
653                //
654                if (errors->message == comment) {
655                        //
656                        // Yup. Increment count, and return now if the count is too high
657                        //
658                        if ( ++errors->nUsed > 5 ) return;
659                        break;
660                }
661                last = errors;
662                errors = errors->next;
663        }
664       
665        if (errors == 0) {
666                //
667                // New error: add it the end of our list
668                //
669                errors = new FredTest3ErrorList;
670                errors->message = comment;
671                errors->nUsed = 1;
672                errors->next = 0;
673                if (errorList) 
674                        last->next = errors;
675                else
676                        errorList = errors;
677        }
678
679        //
680        // Output the message
681        //     
682        logger << "% " << comment;
683        if (errors->nUsed == 5) logger << " (any further such errors suppressed)";
684        logger << G4endl;
685       
686        logger << ++(*nError) << " " << p.x() << " " << p.y() << " " << p.z() 
687                              << " " << v.x() << " " << v.y() << " " << v.z() << G4endl;
688}
689
690
691//
692// ClearErrors
693//
694// Reset list of errors (and clear memory)
695//
696void FredTest3::ClearErrors()
697{
698        FredTest3ErrorList *here, *next;
699       
700        here = errorList;
701        while( here ) {
702                next = here->next;
703                delete here;
704                here = next;
705        }
706}
707
708
709//
710// GetLoggedPV
711//
712// Get the p and v vectors stored in a test3 log file
713//
714G4int FredTest3::GetLoggedPV( std::istream &logger, const G4int errorIndex,
715                              G4ThreeVector &p, G4ThreeVector &v        ) const
716{
717        logger >> std::setprecision(14);                // I wonder if this is necessary?
718        logger.seekg(0);
719
720        //
721        // Search for the requested error index, skipping comments along the way
722        //
723        for(;;) {
724                while( logger.peek() == '%' ) logger.ignore( 99999, '\n' );
725               
726                if (logger.peek() == EOF) return 1;
727               
728                G4int thisIndex;
729               
730                logger >> thisIndex;
731               
732                if (thisIndex == errorIndex) break;
733               
734                logger.ignore( 99999, '\n' );
735        }
736       
737        //
738        // Extract the vectors
739        //
740        // We should probably have some error checking here...
741        //
742        G4double x, y, z;
743       
744        logger >> x >> y >> z;
745        p = G4ThreeVector(x*mm,y*mm,z*mm);
746       
747        logger >> x >> y >> z;
748        v = G4ThreeVector(x*mm,y*mm,z*mm);
749
750        return 0;
751}
752
753
754//
755// --------------------------------------
756// FredTest3PointList stuff
757//
758
759FredTest3PointList::FredTest3PointList( G4int size )
760{
761        pointList = new G4ThreeVector[size];
762        maxPoints = size;
763        numPoints = 0;
764}
765
766FredTest3PointList::~FredTest3PointList()
767{
768        delete [] pointList;
769}
770
771void FredTest3PointList::AddPoint( G4ThreeVector newPoint )
772{
773        if (numPoints < maxPoints) {
774                //
775                // Since we still have space, append the point on the
776                // end of the list
777                //
778                pointList[numPoints] = newPoint;
779                numPoints++;
780        }
781        else {
782                //
783                // Our list is filled up, so replace a random
784                // entry
785                //
786                G4int irand = G4int(G4UniformRand()*( (G4double)maxPoints ));
787                pointList[irand] = newPoint;
788        }
789}
Note: See TracBrowser for help on using the repository browser.