389 {
390 cout <<
"JBeaconSimulator version " <<
JBSversion << endl
391 << endl ;
392
393 string ofname ;
394 string detectorFile ;
395
396 JModuleLocation source_location ;
398 bool useTracker ;
399 unsigned int nscatmax ;
400 unsigned long int nphotons ;
401 double Labs ;
402 double opening_angle_deg ;
403 double beacon_theta_deg ;
404 double beacon_phi_deg ;
405 double lHG ;
406 double gHG ;
407 double lR ;
408 double aR ;
410 double target_step_size_deg ;
411 double tangential_step_size_deg ;
413 int nsamples ;
414 bool write_paths ;
415 bool no_remapping ;
416
417 try {
419
426 zap[
"N-integration-samples"] =
make_field(nsamples) = 1e6 ;
428 zap[
"write-paths"] =
make_field(write_paths) ;
430 zap[
"opening-angle"] =
make_field(opening_angle_deg) = 45 ;
431 zap[
"beacon-theta-deg"] =
make_field(beacon_theta_deg) = 56.2893-15 ;
432 zap[
"beacon-phi-deg"] =
make_field(beacon_phi_deg) = -59.9998 ;
437 zap[
"stepsizes"] =
make_field(stepsizes) = empty_vector ;
438 zap[
"target-step-size-deg"] =
make_field(target_step_size_deg) = 1.0 ;
439 zap[
"tangential-step-size-deg"] =
make_field(tangential_step_size_deg) = 1.0 ;
440 zap[
"no-remapping"] =
make_field(no_remapping) ;
441
442 if (zap.
read(argc, argv) != 0) exit(1) ;
443 }
444 catch(const exception &error) {
445 cerr << error.what() << endl ;
446 exit(1) ;
447 }
448
449
450 {
452 extensions.push_back(".root") ;
453 extensions.push_back(".paths") ;
455 size_t pos = ofname.find(*it) ;
456 if( pos != string::npos ) {
457 ofname = ofname.substr(0,pos) ;
458 }
459 }
460 }
461
462
463 if( !useTracker ) {
464 const int ndefvals = 6 ;
465 double defvals[ndefvals] = { 8, 5, 4, 3, 2, 1 } ;
466 while( stepsizes.size()<nscatmax ) {
467 double val = defvals[min(ndefvals-1,(int)stepsizes.size())] ;
468 stepsizes.push_back(val) ;
469 }
470 }
471
472
474 try {
475 load(detectorFile, detector);
476 }
478 cout << "FATAL ERROR: could not open detector file '" << detectorFile << "'." ;
479 exit(1) ;
480 }
482
483
484
485 gRandom = new TRandom3(0) ;
486
487
488 const double DOM_radius = 0.2159 ;
489
490
491
492
493
494
495
497 JAxis3D source_axis(
getBeaconAxis(source_module,DOM_radius+0.00001,beacon_theta_deg*M_PI/180,beacon_phi_deg*M_PI/180) ) ;
499
500 cout << "SOURCE (NANOBEACON)" << endl
501 << "Positioned on string " << source_location.getString() << " floor " << source_location.getFloor()
502 <<
" (DOM center @ " <<
getDOMcenter(source_module) <<
")" << endl
503 << "Position in local DOM coordinates: theta = "
504 << beacon_theta_deg << " deg, "
505 << "phi = " << beacon_phi_deg << " deg" << endl
506 <<
"distance w.r.t. DOM center = " << (source_axis.getPosition()-
getDOMcenter(source_module)).
getLength() <<
" m" << endl
507 <<
"unit vector from DOM center to beacon position = " <<
JVersor3D(source_axis.getPosition()-
getDOMcenter(source_module)) << endl
508 << "beam direction = " << source_axis.getDirection() << endl
509 << "beam opening angle = " << opening_angle_deg << " deg" << endl
510 << "surface normal = " << surface_normal << endl
511 << endl ;
512
513
514 if( target_pmt.size() != 3 ) {
515 cerr << "FATAL ERROR: provide a target PMT in the format "
516 << "\"string floor daq_channel\"." << endl ;
517 exit(1) ;
518 }
519
520
521 if( !useTracker ) {
522 if( target_pmt[2]<0 || target_pmt[2]>30 ) {
523 cerr << "FATAL ERROR: invalid target pmt index ("
524 << target_pmt[2] << ")" << endl ;
525 exit(2) ;
526 }
527 }
528
529 JModuleLocation target_location(target_pmt[0],target_pmt[1]) ;
531 JAxis3D target_axis(
getDOMcenter(target_module), target_module.getPMT(target_pmt[2]).getDirection() ) ;
532
533
535 opening_angle_deg*M_PI/180,
536 true,
537 surface_normal ) ;
539
540
541 cout << "PHYSICS" << endl ;
543 cout << "Henyey-Greenstein scattering length = " << lHG << " [m], g = " << gHG << endl ;
545 cout << "Rayleigh scattering length = " << lR << " [m], a = " << aR << endl ;
547 cout << "Absorption length = " << Labs << " [m]" << endl ;
548
549
550
551 const double water_depth = 3000 ;
552 const double pressure = 0.1*water_depth ;
554 const double nbwavelength = 470 ;
555 const double index_of_refraction = jd.getIndexOfRefractionGroup(nbwavelength) ;
556 const double cw = JTOOLS::getSpeedOfLight() / index_of_refraction ;
557 cout << "Assuming speed of light in water = " << cw << " [m/ns]" << endl ;
558 cout << endl ;
559
560
561
563
564
565
566
567 cout << "TARGET" << endl ;
568 const double pmt_opening_angle_deg = 30 ;
569 const double pmt_opening_angle_rad = pmt_opening_angle_deg*M_PI/180.0 ;
570 const double pmt_ctmin = cos(0.5*pmt_opening_angle_rad) ;
571 cout << "DOM: string " << target_pmt[0]
572 << ", floor " << target_pmt[1]
574 << "Target radius = " << DOM_radius << " [m]" << endl
575 << "PMT: " ;
576 if( useTracker ) cout << "all 31 PMTs" ;
577 else cout << "PMT " << target_pmt[2] << ", direction = " << target_axis.getDirection() ;
578 cout << endl
579 << "PMTs are simulated as spherical caps" << endl
580 << "PMT opening angle = " << pmt_opening_angle_deg << " deg." << endl
581 << endl ;
582
584 if( useTracker ) {
585
586
590 } else {
591
592 trg =
new JPMTTarget(target_axis,pmt_opening_angle_rad,DOM_radius) ;
593 }
594
595 unsigned long int n_generated_photons = nphotons ;
596 if( useTracker ) {
597
598 cout << "Generating photon paths by photon tracking" << endl ;
601 n_generated_photons,
602 src, sm, trg,
603 Labs ) ;
604 cout << "Photon hits: " << paths.size() << " / " << n_generated_photons << endl ;
605 cout << endl ;
606 }
607
611 if( !useTracker ) {
613 if( no_remapping ) {
614
615
616
617
619 cout << "WARNING: coordinate remapping is deactivated!" << endl ;
620 }
623 int nsteps_save = 250 ;
624 int nsteps_burn_in = 10*nsteps_save ;
625 for( int nscat=0 ; nscat<=(int)nscatmax ; ++nscat ) {
626 cout << "nscat = " << nscat << endl ;
627
628 double stepsize = 1 ;
629 if( nscat>0 && (int)stepsizes.size()>=nscat ) stepsize = stepsizes[nscat-1] ;
631
632 if( nscat != 0 ) cout << "step size = " << stepsize << " [m]" << endl ;
633
634
637 cout << "path = " << endl ;
640 cout << "start_rho = " << start_rho << endl ;
641 if( start_rho == 0 ) {
642 cout << "No possible photon path found with nscat = " << nscat <<". Skipping nscat = " << nscat << "." << endl
643 << "WARNING: note that this does not necessarily mean there are no such paths, simply that the search algorithm could not find one." << endl
644 << endl;
645 continue ;
646 }
647
648
649
650
651
652
653
655 n_generated_photons,
656 start_path,
657 src, sm, trg,
658 Labs,
659 nsteps_burn_in, nsteps_save ) ;
662 if( nscat != 0 ) {
663 cout <<
"Accepted " << naccepted <<
" / " << nsteps <<
" steps (" << mpg.
getFractionAccepted()*100.0 <<
"%)" << endl
666 cout << "NOTE: as a rule of thumb, the optimal value is around ~23% (this can be achieved by modifying the various step sizes)" << endl ;
667 }
668 paths.insert(paths.end(), these_paths.begin(), these_paths.end()) ;
669 cout << endl ;
670
671 if( these_paths.size() == 0 ) {
672 cerr << "Something went wrong. The MCMC generator returned no paths." << endl ;
673 Pscat[nscat] = 0.0 ;
674 Perr[nscat] = 0.0 ;
675 } else {
676 cout << "Creating JMarkovEnsembleIntegrator" << endl ;
678
679 cout << "Integrating" << endl ;
682 Pscat[nscat] =
getMean(result) ;
683 Perr[nscat] =
getSigma(result) / sqrt(nsamples) ;
684
685 integrators.push_back(jmi) ;
686 }
687 cout << endl ;
688 }
689 }
690
691 if( paths.size()==0 ) {
692 cerr << "FATAL ERROR: no paths were generated!" << endl ;
693 exit(1) ;
694 }
695
696
697
698
699 unsigned int npaths = paths.size() ;
704 for( unsigned int i=0; i<npaths; ++i ) {
705 path_lengths[i] = paths[i].getLength() ;
706 xs[i] = paths[i].back().getX() ;
707 ys[i] = paths[i].back().getY() ;
708 zs[i] = paths[i].back().getZ() ;
709 int nscat = paths[i].size()-2 ;
710 if( nscat>(int)nscatmax ) nscatmax = nscat ;
711 }
712
713
720
721
723
724 if( !useTracker ) {
725
727 }
728
729 if( useTracker ) {
732
733 for( unsigned int i=0; i<npaths; ++i ) {
734
735
738 for( unsigned int pmt=0 ; pmt<31 ; ++pmt ) {
739 double ct = target_module.getPMT(pmt).getDirection().getDot(hit_direction) ;
740 if( ct>pmt_ctmin ) hitPMTs.push_back(pmt) ;
741 }
742 if( hitPMTs.size()==1 ) {
743 PMT_is_hit[i] = hitPMTs[0] ;
744 ++nHitsPerPMT[ hitPMTs[0] ] ;
745 }
746 if( hitPMTs.size()>1 ) {
747 cerr << "FATAL ERROR: hit " << hitPMTs.size() << " PMTs simultaneously" << endl ;
748 exit(1) ;
749 }
750 }
751
752 for( int pmt=0 ; pmt<31 ; ++pmt ) {
753 if( nHitsPerPMT[pmt]>0 ) {
754 cout << "PMT" << setw(2) << pmt << ": " << setw(15) << nHitsPerPMT[pmt] << " hits" << endl ;
755 }
756 }
757 cout << endl ;
758 }
759
760
761 const double dt = 0.1 ;
762 const unsigned int margin = 5 ;
763 double Lmin = *min_element(path_lengths.begin(),path_lengths.end() ) ;
764 double Lmax = *max_element(path_lengths.begin(),path_lengths.end() ) ;
765 double tmin = floor( Lmin / cw - margin ) ;
766 double tmax = ceil( Lmax / cw + margin ) ;
767 int nbinst = (int) round( (tmax-tmin)/dt ) ;
768
769 int pmtfront = 0 ;
770 int pmtback = 31 ;
771 if( !useTracker ) {
772
773 pmtfront = target_pmt[2] ;
774 pmtback = target_pmt[2]+1 ;
775 }
776
777 for( int pmt=pmtfront ; pmt<pmtback ; ++pmt ) {
778
779 char fname[500] ;
780 sprintf( fname, "%s_PMT%i.root", ofname.c_str(), pmt ) ;
781 TFile* f = new TFile(fname,"recreate") ;
782
783
784 TH1D hpulse("hpulse","Instantaneous flash pulse profile;time since flash [ns];hit probability / ns",
785 nbinst,tmin,tmax) ;
787 for( int nscat=0 ; nscat<=(int)nscatmax ; ++nscat ) {
788 char hname[200] ;
789 sprintf(hname,"hpulse_nscat%i",nscat) ;
790 hpulse_per_nscat[nscat] = new TH1D(hname,"Instantaneous flash pulse profile;time since flash [ns];hit probability / ns",
791 nbinst,tmin,tmax ) ;
792 }
793 TH2D hXY("hXY","hit position;X;Y",
794 100,xmin,xmax,
795 100,ymin,ymax ) ;
796 hXY.SetOption("colz") ;
797 TH2D hXZ("hXZ","hit position;X;Z",
798 100,xmin,xmax,
799 100,zmin,zmax ) ;
800 hXZ.SetOption("colz") ;
801 TH2D hYZ("hYZ","hit position;Y;Z",
802 100,ymin,ymax,
803 100,zmin,zmax ) ;
804 hYZ.SetOption("colz") ;
805 TH1D hnscat("hnscat",";N_{scat};probability",nscatmax+2,-0.5,nscatmax+1.5) ;
806
807 TH3D hregion("hregion","Photon paths;X;Y;Z",
808 100,
811 100,
814 100,
817
818
820 for( unsigned int i=0 ; i<npaths ; ++i ) {
821 if( PMT_is_hit[i] != pmt ) continue ;
822
823 paths_to_write.push_back(paths[i]) ;
824
825 const double path_length = paths[i].getLength() ;
826 double t = path_length/cw ;
827 int nscat = paths[i].size()-2 ;
828
829 double w = 1 ;
830 if( useTracker ) w = 1.0 / n_generated_photons ;
831 else w = Pscat[nscat] / n_generated_photons ;
832
833 hpulse.Fill(t,w) ;
834 hnscat.Fill(nscat,w) ;
835 hpulse_per_nscat[nscat]->Fill(t,w) ;
836
837 hXY.Fill(xs[i],ys[i],w) ;
838 hXZ.Fill(xs[i],zs[i],w) ;
839 hYZ.Fill(ys[i],zs[i],w) ;
840
841 if( paths.size()<10000 ) {
842 const double dl = 0.001 ;
843 const double dw = w*dl/path_length ;
844 double l_along_seg = 0.5*dl ;
845 int nfilled = 0 ;
846 for( int nv=0; nv<(int)paths[i].size()-1; ++nv ) {
847 double seg_length = paths[i][nv+1].getDistance(paths[i][nv]) ;
848 JVersor3D seg_dir( paths[i][nv+1] - paths[i][nv] ) ;
849 while( l_along_seg<seg_length ) {
851 hregion.Fill( pos.getX(), pos.getY(), pos.getZ(), dw ) ;
853 cerr << "Woohoooo" << endl ;
854 exit(1) ;
855 }
856 ++nfilled ;
857 l_along_seg += dl ;
858 }
859 l_along_seg -= seg_length ;
860 }
861 }
862 }
863
864
865 hregion.Scale(1e20) ;
866
867
868 if( write_paths ) {
869 char path_fname[500] ;
870 sprintf( path_fname, "%s_PMT%i.paths", ofname.c_str(), pmt ) ;
871 cout << "Writing paths." << endl ;
873 cout << endl ;
874 }
875
876
877 if( useTracker ) {
878 for( Int_t bin=1 ; bin<=hnscat.GetNbinsX() ; ++bin ) {
879 double val = hnscat.GetBinContent(bin) ;
880 double nentries = max(1.0, val*n_generated_photons ) ;
881 hnscat.SetBinError(bin, sqrt(nentries)/n_generated_photons) ;
882 }
883 }
884
885
886 if( !useTracker ) {
887 for( int nscat=0 ; nscat<=(int)nscatmax ; ++nscat ) {
888 Int_t bin = hnscat.GetXaxis()->FindBin(nscat) ;
889 double val = hnscat.GetBinContent(bin) ;
890 double err = Perr[nscat]/Pscat[nscat]*val ;
891 hnscat.SetBinError(bin,err) ;
892 }
893 }
894
895
896 hpulse.Scale(1.0/dt) ;
897 for( int nscat=0 ; nscat<=(int)nscatmax ; ++nscat ) {
898 hpulse_per_nscat[nscat]->Scale(1.0/dt) ;
899 }
900
901
902
903 TH1D* hpulse_cum = (TH1D*) hpulse.Clone("hpulse_cum") ;
904 hpulse_cum->GetYaxis()->SetTitle("cumulative hit probability") ;
905 for( Int_t bin=1 ; bin<=hpulse_cum->GetNbinsX() ; ++bin ) {
906 double val = hpulse_cum->GetBinContent(bin-1) ;
907 val += hpulse_cum->GetBinContent(bin) * hpulse_cum->GetXaxis()->GetBinWidth(bin) ;
908 hpulse_cum->SetBinContent(bin,val) ;
909 }
910
911 f->Write() ;
912
913 f->mkdir("EnsembleIntegratorHistograms")->cd() ;
915 (*it)->writeHistograms() ;
916 }
917 f->cd() ;
918
919 f->Close() ;
920
921 cout << "Output in '" << fname << "'." << endl ;
922 }
923 cout << "Done!" << endl ;
924 cout << endl ;
925}
JAxis3D getBeaconAxis(const JModule &dom, double r, double beacon_theta, double beacon_phi)
Get the position and orientation of the nanobeacon on a DOM.
void printResult(vector< double > &v)
void writePaths(string ofname, vector< JPhotonPath > paths)
#define make_field(A,...)
macro to convert parameter to JParserTemplateElement object
Finds photon paths from a nanobeacon source to a target PMT that have a non-zero probability.
JPhotonPath solve(int nscat, const JDirectedSource *src, const JPMTTarget *trg)
Router for direct addressing of module data in detector data structure.
Data structure for a composite optical module.
double getY() const
Get y position.
double getLength() const
Get length.
double getZ() const
Get z position.
double getX() const
Get x position.
Implementation of the JScatteringModel interface with that combines two scattering models into one ef...
Implementation of the JSourceModel class that represents a directed source with a flat intensity dist...
Implementation of the JScatteringModel interface with scattering according to the Henyey-Greenstein f...
Implementation of the JTargetModel class that represents a spherically symmetric target.
Implementation of JMarkovEnsembleIntegrator interface with 1D histograms.
vector< double > integrate(int N, int nscat, JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs)
Integrate with N samples.
The JMarkovPathGenerator generates ensembles of photon paths using a Markov Chain Monte Carlo (MCMC).
int getNsteps()
get the number of Markov steps taken in the last call to generateEnsemble (after burn-in)
void setRadialStepSize_m(double val)
set the average step size in [m] in the radial direction for the scattering vertices
void setCoordinateRemapping(bool val=true)
activate or deactive coordinate remapping
void setTargetStepSize_deg(double val)
set the average step size in degrees for the impact point on the target
void setTangentialStepSize_deg(double val)
Set the average step size theta in degrees for steps in the tangential direction for the scattering v...
double getFractionAccepted_radial()
get the fraction of steps that were accepted when a radial step was performed during the last call to...
int getNacceptedSteps()
get the number of accepted steps taken during the last call to generateEnsemble (after burn-in)
std::vector< JPhotonPath > generateEnsemble(int n, const JPhotonPath &start_path, JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs, int nsteps_burn_in, int nsteps_save)
Generate an ensemble of n paths with a fixed number of scatterings by MCMC-sampling the given scatter...
double getFractionAccepted_tangential()
get the fraction of steps that were accepted when a tangential step was performed during the last cal...
double getFractionAccepted()
get the fraction of accepted steps during the last call to generateEnsemble (after burn-in)
The JMarkovPhotonTracker generates ensembles of photon paths by tracking photons from a source to a t...
std::vector< JPhotonPath > trackPhotons(unsigned long int n, JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs)
Track n photons.
Implementation of the JTargetModel class that represents a single PMT on a DOM.
string getString()
get a string with the path vertex positions
Implementation of the JScatteringModel interface with Rayleigh scattering.
Virtual base class for a scattering model.
void setPosition(JPosition3D &_pos)
Virtual base class for a light detector ("photon target").
void setPosition(JPosition3D &_pos)
void setRadius(double _r)
const JPosition3D & getPosition() const
Utility class to parse command line options.
int read(const int argc, const char *const argv[])
Parse the program's command line options.
Implementation of dispersion for water in deep sea.
const JModule & getModule(const JType< JDetector_t > type, const int id, const JLocation &location=JLocation())
Get module according given detector type.
void load(const std::string &file_name, JDetector &detector)
Load detector from input file.
double getPhotonPathProbabilityDensity(JPhotonPath &p, JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs)
Return the probability density for a photon path with the given ingredients.