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.