Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

FluxSource.cxx

Go to the documentation of this file.
00001 //Header: /cvs/glastsim/flux/FluxSource.cxx,v 1.20 1998/12/18 23:42:02 hsg Exp $
00002 
00003 #include "FluxSvc/FluxSource.h"
00004 
00005 #include "dom/DOM_Element.hpp"
00006 #include "dom/DOM_NodeList.hpp"
00007 #include "xml/Dom.h"
00008 
00009 #include "SpectrumFactoryTable.h"
00010 
00011 #include "SimpleSpectrum.h"
00012 
00013 #include "CLHEP/Random/RandFlat.h"
00014 
00015 #include "geometry/CoordTransform.h"
00016 #include "geometry/Ray.h"
00017 #include "geometry/Box.h"
00018 
00019 #include "Orbit.h"
00020 #include "astro/SkyDir.h"
00021 
00022 #include "FluxException.h" // for FATAL_MACRO
00023 
00024 double  FluxSource::s_backoff;
00025 double  FluxSource::s_radius=1.0;
00026 
00027 // display/command interfaces...
00028 #include <algorithm>
00029 #include <list>
00030 using std::min;
00031 using std::max;
00032 
00033 
00034 FluxSource::FluxSource(ISpectrum* aSpec, double aFlux)
00035 : EventSource(aFlux),  m_spectrum(0),
00036 m_maxEnergy(100.),  // note defualt maximum kinetic energy
00037 _minCos(-0.4f), _maxCos(1.0f), _minPhi(0.0f), _maxPhi(2*M_PI),
00038 m_rmin(0), m_rmax(1), _phi(0.0f), _theta(0.0f), m_pointtype(NOPOINT),
00039 m_launch(NONE), illumBox(0), m_energyscale(GeV) ,m_degreespread(0)
00040 {
00041     s_backoff = 0.;
00042     spectrum(aSpec);
00043     useSpectrumDirection(); // and will use its direction generation
00044     setAcceptance();
00045     //  transformDirection();
00046 }
00047 
00048 
00049 FluxSource::FluxSource(double aFlux, ISpectrum* aSpec,  double l, double b)
00050 : EventSource(aFlux),  m_spectrum(0),
00051 m_maxEnergy(100.),  // note defualt maximum kinetic energy
00052 _minCos(-0.4f), _maxCos(1.0f), _minPhi(0.0f), _maxPhi(2*M_PI),
00053 m_rmin(0), m_rmax(1), _phi(0.0f), _theta(0.0f), m_pointtype(NOPOINT),
00054 m_launch(NONE), illumBox(0),m_gall(l),m_galb(b),m_degreespread(0)
00055 {
00056     s_backoff = 0.;
00057     spectrum(aSpec);
00058     //useSpectrumDirection();  // and will use its direction generation
00059     //setLaunch(*direction);
00060     
00061     getGalacticDir(l,b);        
00062     
00063     m_launch=GALACTIC;
00064     setAcceptance();
00065     //  transformDirection();
00066 }
00067 
00068 
00069 FluxSource::FluxSource(const DOM_Element& xelem )
00070 : EventSource (xelem), m_spectrum(0),
00071 m_maxEnergy(100.),  // note defualt maximum kinetic energy
00072 _minCos(-0.4f), _maxCos(1.0f), _minPhi(0.0f), _maxPhi(2*M_PI),
00073 m_rmin(0), m_rmax(1), _phi(0.0f), _theta(0.0f), m_pointtype(NOPOINT), m_launch(NONE),
00074 illumBox(0), m_energyscale(GeV),m_degreespread(0)
00075 {
00076     static double d2r = M_PI/180.;
00077     
00078     ISpectrum*   s = 0;
00079     std::string class_name;
00080     std::string source_params; 
00081     
00082     DOM_Element   spec = xml::Dom::findFirstChildByName(xelem, "spectrum");
00083     
00084     if (spec == DOM_Element()) {
00085         
00086         // source has no imbedded spectrum element: expect a name
00087         //class_name = xelem.getAttribute("name").transcode();
00088         class_name = xml::Dom::transToChar(xelem.getAttribute("name"));
00089         useSpectrumDirection(); // and will use its direction generation
00090         
00091     } else {
00092         // process spectrum element
00093         DOM_NodeList children = spec.getChildNodes();
00094         
00095         // First child element is type of spectrum
00096         DOM_Node    childNode = children.item(0);
00097         DOM_Element specType;
00098         
00099         if (childNode.getNodeType() == DOM_Node::ELEMENT_NODE) {
00100             specType = (DOM_Element &) childNode;
00101         }
00102         else specType = xml::Dom::getSiblingElement(childNode);
00103         
00104         DOMString   typeTagName = specType.getTagName();
00105         std::string spectrum_name = xml::Dom::transToChar(spec.getAttribute("name"));//.transcode();
00106         //std::string spectrum_frametype = spec.getAttribute("frame").transcode();
00107         std::string spectrum_energyscale = xml::Dom::transToChar(spec.getAttribute("escale"));//.transcode();
00108         
00109         //if(spectrum_frametype == "galactic"){ m_frametype=GALAXY;
00110         //}else if(spectrum_frametype == "glast"){ m_frametype=GLAST;
00111         //}else if(spectrum_frametype == "earth"){ m_frametype=EARTH;
00112         //}
00113         
00114         if(spectrum_energyscale == "GeV"){ m_energyscale=GeV;
00115         }else if(spectrum_energyscale == "MeV"){ m_energyscale=MeV;
00116         }else{
00117             std::cout << "bad energy scale declaration on spectrum:"
00118                 << spectrum_energyscale << " , exiting.";
00119             return;} //this line "just in case"
00120         
00121         
00122         if (typeTagName.equals("particle")) s = new SimpleSpectrum(specType);
00123         else if (typeTagName.equals("SpectrumClass")) {
00124             // attribute "name" is the class name
00125             class_name = xml::Dom::transToChar(specType.getAttribute("name"));//.transcode();
00126             source_params= xml::Dom::transToChar(specType.getAttribute("params"));//.transcode();
00127         }
00128         else {
00129             // no, the tag itself
00130             class_name = xml::Dom::transToChar(typeTagName);//.transcode();
00131         }
00132         // second child element is angle
00133         DOM_Element angles = xml::Dom::getSiblingElement(specType);
00134         DOMString anglesTag = angles.getTagName();
00135         
00136         if (anglesTag.equals("solid_angle") ) {
00137             setLaunch(atof(xml::Dom::transToChar(angles.getAttribute("theta"))) * d2r, 
00138                 atof(xml::Dom::transToChar(angles.getAttribute("phi"))) *d2r);
00139             setCosThetaRange(atof(xml::Dom::transToChar(angles.getAttribute("mincos"))),
00140                 atof(xml::Dom::transToChar(angles.getAttribute("maxcos")))  );
00141         }
00142         else if (anglesTag.equals("direction") ) {
00143             setLaunch(atof(xml::Dom::transToChar(angles.getAttribute("theta"))) * d2r, 
00144                 atof(xml::Dom::transToChar(angles.getAttribute("phi"))) *d2r);
00145         }
00146         else if (anglesTag.equals("use_spectrum") ) {
00147             std::string frame = xml::Dom::transToChar(angles.getAttribute("frame"));
00148             //if(angles.getAttribute("frame")/*.transcode()*/ == "galaxy"){          
00149             if(frame == "galaxy"){
00150                 m_launch=SPECGAL;
00151             }else{
00152                 useSpectrumDirection();
00153             }
00154         }
00155         else if(anglesTag.equals("galactic_dir")){
00156             m_galb=atof(xml::Dom::transToChar(angles.getAttribute("b")));
00157             m_gall=atof(xml::Dom::transToChar(angles.getAttribute("l")));
00158             getGalacticDir(atof(xml::Dom::transToChar(angles.getAttribute("l"))),
00159                 atof(xml::Dom::transToChar(angles.getAttribute("b"))) );
00160             m_launch=GALACTIC;
00161         }
00162         else if(anglesTag.equals("celestial_dir")){
00163             //just like galactic direction, but we'll need to get l and b first:
00164             double ra=atof(xml::Dom::transToChar(angles.getAttribute("ra")));
00165             double dec=atof(xml::Dom::transToChar(angles.getAttribute("dec")));
00166             astro::SkyDir tempdir(ra,dec);
00167             m_galb=tempdir.b();
00168             m_gall=tempdir.l();
00169             //std::cout << "l = " << m_gall << " , b = " << m_galb << " , ra = " <<
00170             //    ra << " , dec = " << dec << std::endl;
00171             getGalacticDir(m_gall,m_galb);
00172             m_launch=GALACTIC;
00173         }
00174         else if(anglesTag.equals("galactic_spread")){
00175             //get l and b, just like for 
00176             m_galb=atof(xml::Dom::transToChar(angles.getAttribute("b")));
00177             m_gall=atof(xml::Dom::transToChar(angles.getAttribute("l")));
00178             m_degreespread = atof(xml::Dom::transToChar(angles.getAttribute("degreespread")));
00179             //getGalacticDir(atof(xml::Dom::transToChar(angles.getAttribute("l"))),
00180             //    atof(xml::Dom::transToChar(angles.getAttribute("b"))) );
00181             m_launch=SPREAD;
00182         }
00183         else {
00184             FATAL_MACRO("Unknown angle specification in Flux::Flux \""
00185                 << xml::Dom::transToChar(anglesTag) << "\"" );
00186         }
00187         
00188         // third child element is optional launch spec
00189         DOM_Element launch = xml::Dom::getSiblingElement(angles);
00190         
00191         if(launch !=DOM_Element()) {
00192             DOMString launchTag = launch.getTagName();
00193             
00194             if(launchTag.equals("launch_point")){
00195                 m_pointtype=SINGLE;
00196                 LaunchType templaunch=m_launch;
00197                 // assume that the launch direction was set by a direction!
00198                 setLaunch(/*launchDir()*/m_launchDir, 
00199                     Point(atof(xml::Dom::transToChar(launch.getAttribute("x"))),
00200                     atof(xml::Dom::transToChar(launch.getAttribute("y"))),
00201                     atof(xml::Dom::transToChar(launch.getAttribute("z"))) ) );
00202                 m_launch=templaunch;
00203                 
00204             }else if(launchTag.equals("patch")){
00205                 
00206                 m_pointtype = PATCH;
00207                 LaunchType templaunch=m_launch;
00208                 
00209                 //setLaunch(launchDir().theta(), launchDir().phi(),
00210                 
00211                 setLaunch( /*m_launchDir*/(-launchDir()).theta(), /*m_launchDir*/(-launchDir()).phi(),
00212                     atof(xml::Dom::transToChar(launch.getAttribute("xmax"))),
00213                     atof(xml::Dom::transToChar(launch.getAttribute("xmin"))),
00214                     atof(xml::Dom::transToChar(launch.getAttribute("ymax"))),
00215                     atof(xml::Dom::transToChar(launch.getAttribute("ymin"))), 
00216                     atof(xml::Dom::transToChar(launch.getAttribute("zmax"))),
00217                     atof(xml::Dom::transToChar(launch.getAttribute("zmin"))) 
00218                     );
00219                 
00220                 m_launch=templaunch;
00221                 
00222                 
00223             }else {
00224                 FATAL_MACRO("Unknown launch specification in Flux::Flux \""
00225                     << xml::Dom::transToChar(launchTag) << "\"" );
00226             }
00227         } 
00228         
00229     }
00230     if( s==0) {
00231         //              std::vector<float> paramvec; parseParamList(source_params, paramvec);
00232         s = SpectrumFactoryTable::instance()->instantiate(class_name, source_params);
00233         if(s==0){
00234             
00235             std::cerr << "List of known Spectrum classes:\n" ;
00236             std::list<std::string>list= SpectrumFactoryTable::instance()->spectrumList();
00237             for( std::list<std::string>::iterator i = list.begin(); i!=list.end(); ++i)
00238                 std::cerr << "\t" << *i << std::endl;
00239             FATAL_MACRO("Unknown Spectrum: "<< class_name);
00240             return;
00241         }
00242     }
00243     
00244     // finally set the spectrum object, and prepare to use it.
00245     FluxSource::spectrum(s);
00246     
00247     s_backoff = 0.;
00248     setAcceptance();
00249     
00250     //transformDirection();
00251     
00252 }
00253 
00254 
00255 FluxSource::~FluxSource()
00256 {
00257     delete m_spectrum;
00258     if(illumBox) delete illumBox;
00259 }
00260 
00261 inline static double    sqr(double x) {return x*x; }
00262 
00263 
00264 void FluxSource::setAcceptance()
00265 {
00266     //Purpose:  set the solid angle of the source
00267     s_radius = sqrt(totalArea() / M_PI ) * 1000;    // radius in mm
00268     if(!s_backoff) s_backoff = s_radius;
00269     
00270     switch (m_launch) {
00271         
00272     case NONE:  // cos theta range...
00273         EventSource::solidAngle( (_maxCos - _minCos)*(_maxPhi - _minPhi) );
00274         break;
00275     case DIRECTION: // single direction solid angle = 1.
00276     case POINT:         // treat as a direction - solid angle = 1.
00277     case GALACTIC:  //single direction, solid angle = 1.
00278     case SPECTRUM:
00279         // make sure that the rate calculation uses the spectrum's count
00280         EventSource::solidAngle( m_spectrum==0? 1.0 : m_spectrum->solidAngle());
00281         break;
00282     case SURFACE:
00283         EventSource::solidAngle( (_maxCos - _minCos) * (_maxPhi - _minPhi) );
00284         if (_maxCos == _minCos) EventSource::solidAngle(1.);  // treat as direction solid angle = 1
00285         break;
00286     case PATCHFIXED:
00287         EventSource::solidAngle(1.0); // single direction, solid angle = 1.
00288         break;
00289     case SPREAD:
00290         EventSource::solidAngle(M_PI*pow(m_degreespread*M_PI/180.,2));
00291         if (m_degreespread == 0.0) EventSource::solidAngle(1.);  // treat as direction solid angle = 1
00292         break;
00293     }
00294 }
00295 
00296 void FluxSource::spectrum(ISpectrum* s, double emax)
00297 {
00298     if (emax > 0) {
00299         setMaxEnergy(emax);
00300         //m_rmax =  s->fraction(emax);
00301         std::cerr << "exercising obsolete function fraction" << std::endl;
00302     }
00303     
00304     m_spectrum = s;
00305     //const char* name = s->particleName();
00306 }
00307 
00308 
00309 FluxSource* FluxSource::event(double time)
00310 {
00311     // Purpose and Method: generate a new incoming particle
00312     // Inputs  - current time
00313     // Outputs - pointer to the "current" fluxSource object.
00314     m_extime = 0;
00315     //iterate through the "veto" loop only if galactic coordinates are given for the source - otherwise,
00316     //the particles originate close to GLAST, and can still be incident.
00317     // loop through until you get a particle which is not occluded by the earth.
00318     
00319     do{
00320         calculateInterval(time+m_extime);
00321         //std::cout << "now interal is " << m_interval << std::endl;
00322         computeLaunch(time+m_extime+m_interval);
00323         //std::cout << "Testing at time = " << time+m_extime+m_interval << " , interval = " << m_interval << std::endl;
00324         //std::cout << "occluded? " << occluded() << std::endl;
00325         m_extime+=m_interval;
00326     }while(occluded() || m_interval == -1);
00327     m_extime -= m_interval;
00328     
00329     
00330     //now set the actual interval to be what FluxMgr will get
00331     m_interval += m_extime;
00332     EventSource::setTime(time+m_interval+m_extime);
00333     correctForTiltAngle();
00334     return this;
00335     // could be a call-back
00336 }
00337 
00338 
00339 
00340 void FluxSource::randomLaunchPoint()
00341 {
00342     //Purpose:  cerate a random launch point
00343     HepRotation r_pln;
00344     
00345     double ly = m_launchDir.y(), lx = m_launchDir.x();
00346     if( lx !=0 || ly !=0 )r_pln.rotate(acos(m_launchDir.z()), 
00347         Vector(-ly, lx, 0.));
00348     
00349     // pick a random position on the planar section of a sphere through 
00350     // its midpoint
00351     double azimuth = RandFlat::shoot( 2*M_PI );
00352     double rad = s_radius*(sqrt(RandFlat::shoot()));
00353     
00354     // create two vectors to describe the particle launch: one to describe
00355     // the point in the plane perpendicular to the launch direction (within
00356     // the cross-section of the sphere containing the instrument) and a 
00357     //second to describe the distance along the normal between the launch 
00358     // point and that plane.
00359     Vector posLaunch(rad*cos(azimuth), rad*sin(azimuth), 0.);
00360     
00361     // rotate these vectors according to the theta, phi specs
00362     CoordTransform  t(r_pln, Vector(0,0,0) ); // Vector(_box->center())
00363     t.transformVector(posLaunch);
00364     
00365     // define actual launch point
00366     m_launchPoint = (Point&)posLaunch - m_launchDir*s_backoff;
00367 }
00368 
00369 
00370 
00371 double FluxSource::flux(double time) const
00372 {
00373     //return enabled() ? std::max(m_spectrum->flux(time),/*0.*/ EventSource::flux(time)) : 0;
00374     if(!enabled()){ return 0;}
00375     if(m_spectrum->flux(time)){ return m_spectrum->flux(time);}
00376     else{return EventSource::flux(time);}
00377 }
00378 
00379 #if 1
00380 double FluxSource::solidAngle() const
00381 {
00382     //  return std::max(m_spectrum->solidAngle(), EventSource::solidAngle());
00383     return  EventSource::solidAngle();
00384 }
00385 #endif
00386 
00387 void FluxSource::computeLaunch (double time)
00388 {
00389     // Purpose: set energy using the Spectrum object (scales momentum)
00390     // Note: since PEGS files crap out at some energ, the max energy must
00391     // be limited
00392     const double fudge=1.001; // in ncase max is only energy, round-off error
00393     double kinetic_energy;
00394     //do {
00395     // kinetic_energy= (*spectrum())(RandFlat::shoot(m_rmin, m_rmax));
00396     //FIXME: make this a class variable
00397     kinetic_energy = spectrum()->energySrc( HepRandom::getTheEngine(), time /*+ m_extime*/ );
00398     //std::cout << "kinetic energy=" << kinetic_energy << " , max=" << m_maxEnergy* fudge << std::endl;
00399     //kinetic_energy = spectrum()->operator ()(HepRandom::getTheEngine()->flat());// time + m_extime );
00400     //}    while (kinetic_energy > m_maxEnergy* fudge);
00401     
00402     // get the launch point and direction, according to the various strategies
00403     switch (m_launch) {
00404     case SPECTRUM:
00405         {
00406             // special option that gets direction from the spectrum object
00407             // note extra - sign since direction corresponds to *from*, not *to*
00408             std::pair<float,float> direction = spectrum()->dir(kinetic_energy,HepRandom::getTheEngine());
00409             double costh = direction.first;
00410             double sinth = sqrt(1.-costh*costh);
00411             double phi = direction.second;
00412             Vector v(cos(phi)*sinth, sin(phi)*sinth, costh);
00413             m_launchDir = -v;
00414             
00415             // extra rotation in case GLAST is not zenith pointing (beware, 
00416             // might be confusing)
00417             // keep x-axis perpendicular to zenith direction
00418             if (_theta != 0.0) m_launchDir.rotateX(_theta).rotateZ(_phi);
00419             //WARNING UNCOMMENTED
00420             if(m_pointtype==NOPOINT){
00421                 randomLaunchPoint();
00422             }
00423             break;
00424         }        
00425     case SPECGAL:
00426         {
00427             //note: the direction is in the form (l,b) here.
00428             std::pair<float,float> direction = spectrum()->dir(kinetic_energy,HepRandom::getTheEngine());
00429             double l = direction.first;
00430             double b = direction.second;
00431             //then set up this direction:
00432             getGalacticDir(l,b);
00433             m_launch = SPECGAL;
00434             break;
00435         }
00436     case GALACTIC:
00437         {
00438             //getGalacticDir should already have been called in the constructor
00439             
00440             getGalacticDir(m_gall, m_galb);
00441             break;
00442         }
00443     case SPREAD:
00444         {
00445             getGalacticDir(m_gall, m_galb);
00446             m_launch = SPREAD;
00447             spreadTheDirection();
00448             correctForTiltAngle();
00449             break;
00450         }
00451     case SURFACE:
00452         {
00453             getSurfacePosDir();
00454             break;
00455         }
00456     case PATCHFIXED: {
00457         // Check for normal incidence...don't want to try to rotate about a 
00458         // zero vector
00459         if (_theta == 0.0) {
00460             // Just choose a random position within the rectangle
00461             double xInc = patchXmin + patchWidX * RandFlat::shoot();
00462             double yInc = patchYmin + patchWidY * RandFlat::shoot();
00463             double zInc = patchTop;
00464             m_launchPoint = Point(xInc, yInc, zInc);
00465         } else {
00466             double dist = FLT_MAX;
00467             const double distToSearch = 500.;
00468             do {
00469                 randomLaunchPoint();   // pick a point on the sphere as usual
00470                 // Test to see if this position pierces our patch
00471                 Ray trialRay(m_launchPoint, m_launchDir);
00472                 dist = illumBox->distanceToEnter(trialRay, distToSearch);
00473                 // set the launchPoint to begin on our surface - the patch
00474                 if (dist < FLT_MAX) m_launchPoint = trialRay.position(dist);
00475                 
00476                 // search til we find a position that satisfies our patch
00477             } while (dist >= FLT_MAX); 
00478         }
00479         break;
00480                      }
00481     case NONE:
00482         {
00483             double  costh = -RandFlat::shoot(_minCos, _maxCos);
00484             double  sinth = sqrt(1.-costh*costh);
00485             double  phi = RandFlat::shoot(_minPhi, _maxPhi);
00486             
00487             // extra rotation in case not zenith pointing (beware, might be
00488             // confusing)
00489             m_launchDir = Vector(cos(phi)*sinth, sin(phi)*sinth, costh);
00490             
00491             // keep x-axis perpendicular to zenith direction
00492             if (_theta != 0.0) m_launchDir.rotateX(_theta).rotateZ(_phi);
00493         }
00494         // fall through to...
00495     case DIRECTION:
00496         {
00497             //unused
00498         }
00499         // fall through to...
00500     case POINT:
00501         // here now that point and direction are set
00502         break;
00503         
00504     } // switch m_launch
00505     
00506     // the service needs to return energy in MeV, so do a conversion if necessary:
00507     if(m_energyscale==MeV){
00508         m_energy = kinetic_energy;
00509     }else if(m_energyscale==GeV){
00510         m_energy = kinetic_energy*1000.;
00511     }
00512     
00513     
00514     switch (m_pointtype) {
00515     case NOPOINT:
00516         {
00517             randomLaunchPoint();
00518             break;
00519         }
00520         
00521     case SINGLE:
00522         {
00523             break;
00524         }
00525     case PATCH:
00526         {
00527             // Check for normal incidence...don't want to try to rotate about a 
00528             // zero vector
00529             if (_theta == 0.0) {
00530                 // Just choose a random position within the rectangle
00531                 double xInc = patchXmin + patchWidX * RandFlat::shoot();
00532                 double yInc = patchYmin + patchWidY * RandFlat::shoot();
00533                 double zInc = patchTop;
00534                 m_launchPoint = Point(xInc, yInc, zInc);
00535             } else {
00536                 double dist = FLT_MAX;
00537                 const double distToSearch = 500.;
00538                 do {
00539                     randomLaunchPoint();   // pick a point on the sphere as usual
00540                     // Test to see if this position pierces our patch
00541                     Ray trialRay(m_launchPoint, m_launchDir);
00542                     dist = illumBox->distanceToEnter(trialRay, distToSearch);
00543                     // set the launchPoint to begin on our surface - the patch
00544                     if (dist < FLT_MAX) m_launchPoint = trialRay.position(dist);
00545                     
00546                     // search til we find a position that satisfies our patch
00547                 } while (dist >= FLT_MAX); 
00548             }
00549             break;
00550         }
00551     }
00552     //   transformDirection(); 
00553     //correctForTiltAngle();
00554 }
00555 
00556 std::string FluxSource::fullTitle () const
00557 {
00558     return title();
00559 }
00560 
00561 std::string FluxSource::displayTitle () const
00562 {
00563     std::strstream s;
00564     s << EventSource::displayTitle() << '(' << m_spectrum->title() ;
00565     s << ')' << '\0';
00566     std::string t(s.str());
00567     s.freeze(false);
00568     return t;
00569 }
00570 
00571 int FluxSource::eventNumber()const
00572 {
00573     return 0;
00574 }
00575 
00576 void  FluxSource::setCosThetaRange(double minc, double maxc)
00577 {
00578     // require _maxCos > _minCos for solid angle calculation
00579     _minCos   = min(minc,maxc);
00580     _maxCos   = max(minc,maxc);
00581     if(_minCos==_maxCos) {
00582         if(_minCos!=-1) _minCos-=0.001; else _maxCos +=0.001;
00583     }
00584     m_launch  = NONE;
00585     setAcceptance();
00586 }
00587 
00588 void  FluxSource::setPhiRange(double min_phi, double max_phi)
00589 {
00590     // Assume angles are given in degrees
00591     _minPhi  = min_phi;
00592     _maxPhi  = max_phi;
00593     m_launch = NONE;
00594     setAcceptance();
00595 }
00596 void FluxSource::setLaunch(const Vector& dir, const Point& pos)
00597 {
00598     //std::cout << "setting launch" << std::endl;
00599     m_launchDir   = dir;
00600     m_launchPoint = pos;
00601     m_launch     = POINT;
00602 }
00603 void FluxSource::useSpectrumDirection()
00604 {
00605     m_launch = SPECTRUM;
00606 }
00607 
00608 
00609 void FluxSource::setLaunch(double theta, double phi)
00610 {
00611     //std::cout << "setting launch" << std::endl;
00612     _theta = theta;
00613     _phi   = phi;
00614     Vector dir(sin(_theta)*cos(_phi),sin(_theta)*sin(_phi),cos(_theta));
00615     setLaunch(-dir); // minus due to z axis pointing UP!
00616 }
00617 
00618 void FluxSource::setLaunch(const Vector& dir)
00619 {
00620     //std::cout << "setting launch" << std::endl;
00621     m_launchDir    = dir.unit();
00622     m_launch      = DIRECTION;
00623     _theta = (-dir).theta();
00624     _phi   = (-dir).phi();
00625     setAcceptance();
00626 }
00627 
00628 void FluxSource::setLaunch(double theta, double phi, double xMax, 
00629                            double xMin, double yMax, double yMin, 
00630                            double zTop, double zBot) {
00631     
00632     //std::cout << "setting launch" << std::endl;
00633     // fixed angle patch    
00634     // Just set up the area of illumination
00635     double epsilon = 1e-5;
00636     
00637     if (zTop < zBot) {
00638         WARNING_MACRO("zTop < zBot -- SWAPPING");
00639         std::swap(zTop, zBot);
00640     }
00641     
00642     patchTop = zTop;
00643     patchBottom = zBot;
00644     patchHeight = fabs(zTop - zBot);
00645     if(patchHeight < epsilon) patchHeight = 0.0;
00646     
00647     if (xMax < xMin) {
00648         WARNING_MACRO("patchXmax < patchXmin in Flux -- SWAPPING");
00649         std::swap(xMax, xMin);
00650     }
00651     
00652     if( yMax < yMin) {
00653         WARNING_MACRO("patchYmax < patchYmin in Flux -- SWAPPING");
00654         std::swap(yMax, yMin);
00655     }
00656     
00657     patchXmax = xMax;
00658     patchXmin = xMin;
00659     patchYmax = yMax;
00660     patchYmin = yMin;
00661     
00662     patchWidX = fabs(patchXmax - patchXmin);
00663     patchWidY = fabs(patchYmax - patchYmin);
00664     if(patchWidX < epsilon) patchWidX = 0.0;
00665     if(patchWidY < epsilon) patchWidY = 0.0;
00666     
00667     if( (patchHeight == 0.0) && ( (patchWidX == 0.0) || (patchWidY == 0.0) ) ) {
00668         FATAL_MACRO("zero area illumination\n");
00669     }
00670     if( (patchWidX == 0.0) && (patchWidY == 0) ) {
00671         FATAL_MACRO("zero area illumination!\n");
00672     }
00673     
00674     // Check if it's on-axis but the height of our box is non-zero
00675     if ((theta == 0.0) && (patchHeight != 0.0)) {
00676         WARNING_MACRO("on-axis, but zTop != zBot, setting zBot = zTop\n");
00677         patchHeight = 0.0;
00678         patchBottom = patchTop;
00679     }
00680     
00681     // If one of the dimensions is missing...create a small one
00682     // We want to use a box - so this forces the issue
00683     if (patchHeight <= 0.0) patchHeight = 0.0001;
00684     if (patchWidX <= 0.0) patchWidX = 0.0001;
00685     if (patchWidY <= 0.0) patchWidY = 0.0001;
00686     
00687     // Setup our box...used to test when we have chosen on position on 
00688     // the sphere that will work
00689     illumBox = new Box(patchWidX, patchWidY, patchHeight);
00690     Vector center((xMax + xMin)/2., (yMax+yMin)/2., (zBot+zTop)/2.);
00691     illumBox->move(center);
00692     
00693     // setup the fixed angles
00694     _theta = theta;
00695     _phi   = phi;
00696     Vector dir(sin(_theta)*cos(_phi),sin(_theta)*sin(_phi),cos(_theta));
00697     // minus dir since z axis is pointing up, see setLaunch(theta, phi)
00698     m_launchDir    = (-dir).unit();
00699     m_launch      = PATCHFIXED;
00700     
00701     setAcceptance();
00702 }
00703 
00704 void FluxSource::setLaunch(double xMax, double xMin, double yMax, double yMin,
00705                            double zTop, double zBot, bool fan)
00706 {
00707     
00708     
00709     // Inputs: bounds of the incident box: xMax, xMin, yMax, yMin, zTop, zBot
00710     //         fan denotes whether or not this is a fan beam
00711     // setup illumination of some portion of the instrument - could be one side
00712     // or a box.  If illuminating one side - it will always be the +X side 
00713     // of GLAST
00714     
00715     double epsilon = 1e-5;
00716     
00717     sidePatch = false;
00718     fanBeam = false;
00719     double thMax = acos(_minCos);  // theta = [0, PI]
00720     double thMin = acos(_maxCos);
00721     
00722     if (zTop < zBot) {
00723         WARNING_MACRO("zTop < zBot -- SWAPPING");
00724         std::swap(zTop, zBot);
00725     }
00726     
00727     patchTop = zTop;
00728     patchBottom = zBot;
00729     patchHeight = fabs(zTop - zBot);
00730     if(patchHeight < epsilon) patchHeight = 0.0;
00731     
00732     if (xMax < xMin) {
00733         WARNING_MACRO("patchXmax < patchXmin in Flux -- SWAPPING");
00734         std::swap(xMax, xMin);
00735     }
00736     
00737     if( yMax < yMin) {
00738         WARNING_MACRO("patchYmax < patchYmin in Flux -- SWAPPING");
00739         std::swap(yMax, yMin);
00740     }
00741     
00742     patchXmax = xMax;
00743     patchXmin = xMin;
00744     patchYmax = yMax;
00745     patchYmin = yMin;
00746     
00747     patchWidX = fabs(patchXmax - patchXmin);
00748     patchWidY = fabs(patchYmax - patchYmin);
00749     if(patchWidX < epsilon) patchWidX = 0.0;
00750     if(patchWidY < epsilon) patchWidY = 0.0;
00751     
00752     // area of the sides of GLAST
00753     double aTop, aBot, aSide0, aSide1, aSide2, aSide3; 
00754     
00755     // Define the areas of the sides that may be hit by the beam.
00756     if( (patchWidX == 0.0) || (patchWidY == 0.0) ) {
00757         
00758         if( (patchHeight == 0.0) || ((patchWidX == 0.0) && (patchWidY == 0.0)) ) {
00759             FATAL_MACRO("zero area illumination\n");
00760         }
00761         
00762         // we're illuminating one side of the instrument
00763         sidePatch = true; 
00764         
00765         fanBeam = fan; // fan beam or not (aka polar beam)?
00766         
00767         if( patchWidY == 0.0) { 
00768             // always shoot into +x-axis, so swap the variables to reflect this
00769             double tempY = patchYmax;
00770             patchWidY = patchWidX;
00771             patchYmax = patchXmax;
00772             patchYmin = patchXmin;
00773             patchWidX = 0.0;
00774             patchXmin = tempY;
00775             patchXmax = tempY;
00776         }
00777         
00778         // compute the areas of the sides that may be illuminated
00779         aSide0 = patchHeight * patchWidY;  // +X axis side
00780         aSide1 = 0.0;
00781         aSide2 = 0.0;
00782         aSide3 = 0.0;
00783         aTop = 0.0;
00784         aBot = 0.0;
00785         
00786     } else { // illuminating more than just one side
00787         // Note this always refers to a polar beam, just not sidePatch
00788         aTop = patchWidX * patchWidY;
00789         aBot = patchWidX * patchWidY;
00790         aSide0 = patchHeight * patchWidY; // +X axis side
00791         aSide1 = patchHeight * patchWidX;
00792         aSide2 = patchHeight * patchWidY;
00793         aSide3 = patchHeight * patchWidX;
00794     }
00795     
00796     // Checking phi range for the case where we're illuminating one side of GLAST
00797     if( (sidePatch == true) && (fanBeam == false) ) { 
00798         //      WARNING_MACRO("illuminating one side with polar beam, PHI = (-PI, PI)");
00799         
00800     } else if ((sidePatch == true) && 
00801         ((_minPhi < (-M_PI/2.) * (1.+epsilon) ) ||
00802         (_maxPhi > (M_PI/2.) * (1.+epsilon)  )   )     )
00803     {  // fan Beam, side Patch
00804         // WARNING_MACRO("minPhi or maxPhi is out of range for illuminating one side with fan beam - reset to (-PI/2, PI/2)");
00805         _minPhi = -M_PI/2.;
00806         _maxPhi = M_PI/2.;
00807         
00808     } else if (sidePatch == false) { // illuminating more than one side of GLAST
00809         //      WARNING_MACRO("sidePatch == false:  require PHI = (-PI, PI)");
00810         _minPhi = -M_PI;
00811         _maxPhi = M_PI;
00812     }
00813     
00814     // total area to be illuminated
00815     double aSideTot = aSide0 + aSide1 + aSide2 + aSide3; 
00816     
00817     patchRange = (_minCos < 0 ? -1 : 1) * _minCos * _minCos -
00818         (_maxCos < 0 ? -1 : 1) * _maxCos * _maxCos;
00819     
00820     patchOffset = (_maxCos < 0 ? -1 : 1) * _maxCos * _maxCos;
00821     
00822     // cout << "patchRange, patchOffset = " << patchRange << " " << patchOffset << "\n";
00823     
00824     float wBot = 0.0, wTop = 0.0;
00825     
00826     if (sidePatch == true) {  
00827         // assumes we're hitting +x side, so no top or bottom
00828         wBot = 0.0;
00829         wTop = 0.0;
00830         
00831     } else {
00832         // Calculate geometric factor weights for Top and Bottom
00833         // based on integration of cos(theta) * d(Omega) about z-axis
00834         if ( (thMax <= M_PI / 2.) && (thMin < M_PI / 2.) ) {  
00835             // will only hit top, not bottom
00836             wBot = 0.0;
00837             wTop = _maxCos * _maxCos - _minCos * _minCos;
00838             // check for normal incidence
00839             if((thMax == 0) && (thMin == 0) ) wTop = 1.0;  
00840             
00841         } else if ( (thMax > M_PI / 2.) && (thMin < M_PI / 2.) ) {
00842             // could hit top or bottom
00843             wBot = _minCos * _minCos;
00844             wTop = _maxCos * _maxCos;
00845             
00846         } else { // (thMax > 90.) && (thMin >= 90.)  // only hit bottom, not top
00847             wBot = _minCos * _minCos - _maxCos * _maxCos;
00848             wTop = 0.0;
00849             if ( (thMax == M_PI) && (thMin == M_PI) ) wBot = 1.0; // bottom only
00850         }
00851     }
00852     
00853     // Calculate Weights for each side, based on integration
00854     // about z-axis of d(area) * d(Omega) =
00855     // (cos(phi) * sin(theta))*sin(theta) * d(phi) * d(theta)
00856     // range of phi is [-PI, PI] for sidePatch = false
00857     float wSide;
00858     
00859     if(sidePatch == true) {
00860         wSide = 1.0;
00861     } else {
00862         wSide = ( (thMax - thMin) - 0.5 * ( sin(2. * thMax) - sin(2. * thMin) ) ) 
00863             / M_PI;
00864     }
00865     
00866     Fratio = wSide * aSideTot / (wSide * aSideTot + wTop * aTop + wBot * aBot);
00867     // cout << "Fratio = " << Fratio << "\n";
00868     // cout << "wSide, wTop, wBot, aSideTot = " << wSide << " " << wTop << " " << wBot << " " << aSideTot << "\n";
00869     // cout << "aTop, aBot, aSide0, aSide1, aSide2, aSide3 = " << aTop << " " << aBot << " " << aSide0 << " " << aSide1 << " " << aSide2 << " " << aSide3 << "\n";
00870     // cout << "thMax, thMin, _minPhi, _maxPhi = " << thMax << " " << thMin << " " << _minPhi << " " << _maxPhi << "\n";
00871     
00872     m_launch = SURFACE;
00873     setAcceptance();
00874 }
00875 
00876 void FluxSource::getSurfacePosDir() {
00877     // "patch" uniform isotropic illumination schemes:
00878     //               more than one side:  4*PI, or delimited by polar cones
00879     //               one side (or part of one side)
00880     //               top / bottom (or part of top / bottom)
00881     //
00882     // Range of azimuthal angle, PHI:
00883     //   if more than one side is illuminated, range = (-PI,PI)
00884     //   if rectangle on one side (+X) is illuminated, range = (-PI/2,PI/2)
00885     //
00886     // One-side illumination:
00887     //   GLAST is quadrilaterally symmetric, so we assume the +X axis side.
00888     //   Side illumination has two options:  a fan beam or polar beam.
00889     //   In the fan-beam case, the polar range (w/ +Z-axis = symmetry axis)
00890     //   and azimuthal ranges can be delimited.  Polar-beam illumination on
00891     //   a side is realized by making the +X-axis the symmetry axis.
00892     //
00893     // 4*PI, delimited cones (> one side), or top/bottom illumination:
00894     //   Presently, the only option is with +Z-axis = symmetry axis
00895     //   with PHI range = (-PI,PI)
00896     //
00897     // Note that a rectangular-solid volume, or rectangular surface,
00898     //   interior to the GLAST instrument can be illuminated.
00899     double xInc, yInc, zInc;
00900     double cosTh, sinTh, phi;
00901     int sideNum;
00902     if (RandFlat::shoot() < Fratio) { // Hit a Side
00903         double cosE, sinE;
00904         
00905         if ( (sidePatch == true) && (fanBeam == false) ) {   
00906             // Polar Beam and Patch on +X side
00907             // Polar Beam, so theta = [ThetaMin, ThetaMax] about +X axis 
00908             // (not +Z axis)
00909             double randNum = RandFlat::shoot() * patchRange + patchOffset;
00910             cosE = ( (randNum < 0) ? -1 : 1) * sqrt( fabs (randNum) );
00911             sinE = sqrt( 1 - cosE * cosE);
00912             
00913             // Xi = [0, 2M_PI];  can be anything
00914             double Xi = RandFlat::shoot(0., 2*M_PI);  
00915             
00916             cosTh = sinE * sin(Xi);    // law of cosines
00917             sinTh = sqrt(1 - cosTh * cosTh);
00918             
00919             double anySidePhi = (Xi < 0 ? -1 : 1) * acos(cosE / sinTh);
00920             phi = anySidePhi;   // phi = [ -PI/2, PI/2]  always hitting +X side
00921             
00922             // already know that sidePatch == true, so we must hit +X side
00923             sideNum = 0;  
00924             
00925         } else {
00926             // either Fan Beam Patch on +X axis side OR illuminating more than
00927             // one side with a Polar Beam, with azimuthal coordinate unrestricted
00928             bool PhiDONE;
00929             do {
00930                 PhiDONE = false;
00931                 cosE = sqrt(RandFlat::shoot());  
00932                 // get cosE = sqrt[0,1] which corresponds to [0, PI/2]
00933                 // cosTheta isn't restricted until the conditional at the end 
00934                 // of the do-while loop
00935                 sinE = sqrt( 1 - cosE * cosE);
00936                 
00937                 double Xi = RandFlat::shoot(-M_PI, M_PI);
00938                 cosTh = sinE * sin(Xi);
00939                 sinTh = sqrt(1 - cosTh * cosTh);
00940                 
00941                 double rand = RandFlat::shoot(-1, 1);
00942                 
00943                 double anySidePhi = (rand < 0 ? -1 : 1) * acos(cosE / sinTh);
00944                 
00945                 sideNum = int(4 * RandFlat::shoot()); // choose which side to hit
00946                 if(sidePatch == true) sideNum = 0;  // always hit +x side
00947                 phi = anySidePhi + (M_PI / 2.) * sideNum;
00948                 
00949                 if(fanBeam == true) {  // is Phi within bounds?
00950                     if ( (phi >= _minPhi) && (phi <= _maxPhi) ) PhiDONE = true;
00951                 } else {
00952                     PhiDONE = true;
00953                 }
00954                 
00955             } while( (cosTh > _maxCos) || (cosTh < _minCos) || (!PhiDONE) );
00956             
00957         }
00958         // choose incident position on side
00959         zInc = patchBottom + patchHeight * RandFlat::shoot();
00960         
00961         if ( (sideNum % 2) == 0) { // hit X side
00962             if(sideNum == 0) {
00963                 xInc = patchXmax;
00964             } else {
00965                 xInc = patchXmin;
00966             }
00967             yInc = patchYmin + patchWidY * RandFlat::shoot();
00968         } else {  // Hit Y side
00969             xInc = patchXmin + patchWidX * RandFlat::shoot();
00970             if (sideNum == 1) {
00971                 yInc = patchYmax;
00972             } else {
00973                 yInc = patchYmin;
00974             }
00975         }
00976     } else {
00977         
00978         // Hit top or bottom, within delimited polar cones; azimuthal 
00979         // coordinate unrestricted
00980         phi = RandFlat::shoot(_minPhi, _maxPhi);
00981         double ranN = RandFlat::shoot() * patchRange + patchOffset;
00982         cosTh = (ranN > 0 ? 1 : -1) * sqrt( fabs ( ranN ) );
00983         sinTh = sqrt( 1 - cosTh * cosTh );
00984         
00985         if (cosTh > 0.) {
00986             zInc = patchTop;
00987         }
00988         else {
00989             zInc = patchBottom;
00990         }
00991         xInc = patchXmin + patchWidX * RandFlat::shoot();
00992         yInc = patchYmin + patchWidY * RandFlat::shoot();
00993     }
00994     
00995     Point IncPoint(xInc, yInc, zInc);
00996     m_launchPoint = IncPoint;
00997     m_launchDir = Vector( -(cos(phi) * sinTh), -(sin(phi) * sinTh), -(cosTh));
00998 }
00999 
01000 void FluxSource::getGalacticDir(double l,double b){
01001     using namespace astro;
01002     SkyDir unrotated(l,b,SkyDir::GALACTIC);
01003     //here is the old mechanism:
01004     //double theta=sqrt(pow(b,2)+pow(l,2))*M_2PI/360.;
01005     
01006     //if (l==0.){l+=0.000000000001;}  //to fix divide-by-zero errors
01007     //double phi = atan(b/l);
01008     
01009     //here we construct the cartesian galactic vector
01010     //OLDVector gamgal(sin(l*M_2PI/360.)*cos(b*M_2PI/360.) , sin(b*M_2PI/360.) , cos(l*M_2PI/360.)*cos(b*M_2PI/360.));
01011     //Vector gamgal(cos(l*M_2PI/360.)*cos(b*M_2PI/360.) , sin(l*M_2PI/360.)*cos(b*M_2PI/360.) , sin(b*M_2PI/360.) );
01012     
01013     //get the transformation matrix..
01014     Rotation celtoglast=GPS::instance()->transformCelToGlast(GPS::instance()->time() + m_interval + m_extime);
01015     
01016     //and do the transform:
01017     //setLaunch(galtoglast*gamgal);
01018     setLaunch(celtoglast*unrotated());
01019     m_launch = GALACTIC;
01020     correctForTiltAngle();
01021 }
01022 
01023 
01024 std::string FluxSource::title () const
01025 {
01026     std::strstream t;
01027     t << m_spectrum->title() << ", ";
01028     switch (m_launch) {
01029     case NONE:
01030         t << "range(" << _minCos << ',' << _maxCos << "), ";
01031         if( theta() == 0) break;
01032     case DIRECTION:
01033         t << "angle(" << theta()*180/M_PI << ','
01034             << phi()*180/M_PI << "), ";
01035         break;
01036     case POINT:
01037         t << "launch(" << m_launchDir << m_launchPoint << "), ";
01038         break;
01039     case SURFACE:
01040         t << "box(" << patchXmin << ',' << patchXmax << ',' << patchYmin << ',' 
01041             << patchYmax << ',' << patchTop << ',' << patchBottom << ") theta(" 
01042             << _minCos << ',' << _maxCos << "), phi(" << _minPhi << ',' 
01043             << _maxPhi << "), ";
01044         break;
01045     case SPECTRUM:
01046         break;
01047     case GALACTIC:
01048         break;
01049     case PATCHFIXED:
01050         t << "box(" << patchXmin << ',' << patchXmax << ',' << patchYmin 
01051             << ',' << patchYmax << ',' << patchTop << ',' << patchBottom 
01052             << ") theta(" << _theta << "), phi(" << _phi << "), ";
01053     }
01054     t << "flux("<< flux(0.) << ")" << '\0';    
01055     std::string s(t.str());
01056 #ifdef WIN32
01057     t.freeze(false);
01058 #endif
01059     return s;
01060 }
01061 
01062 
01063 void FluxSource::refLaunch(LaunchType launch) {m_launch=launch;}
01064 void FluxSource::refPoint(PointType point) {m_pointtype=point;}
01065 
01066 double FluxSource::calculateInterval (double time){   
01067     //return std::max(m_spectrum->interval(time),/*0.*/ EventSource::interval(time));
01068     double intrval=m_spectrum->interval(time/* + m_extime*/);
01069     if(intrval!=-1){m_interval = intrval/* + m_extime*/;
01070     }else{
01071         m_interval = explicitInterval(time/*+m_extime*/);
01072     }
01073     return m_interval;
01074 }
01075 
01076 
01077 double FluxSource::explicitInterval (double time)
01078 {
01079     double  r = (solidAngle()*flux(time)*6.);
01080     
01081     if (r == 0){ return -1.;// - m_extime; //the minus is for ensuring that interval() returns a -1 flag.
01082     }else{  
01083         double p = RandFlat::shoot(1.);
01084         return ((-1.)*(log(1.-p))/r) /*+m_extime*/;
01085     }
01086 }
01087 
01088 bool FluxSource::occluded(){
01089     double current,max,z;
01090     //Purpose:  to determine whether or not the current incoming particle will be blocked by the earth.
01091     //Output:  "yes" or "no"
01092     //REMEMBER:  the earth is directly below the satellite, so, to determine occlusion,
01093     // we must assume the frame to be checked against is zenith-pointing, and hence, we want 
01094     //the direction of the particle BEFORE it is compensated for tilt angles.
01095     
01096     z=this->rawDir().z();
01097     //std::cout << "z = " << z << std::endl;
01098     current=asin( fabs(this->/*launchDir*/rawDir().z()) / 1.);//(this->launchDir().magnitude()) is always 1. 
01099     max = acos(-0.4)-(M_PI/2.);
01100     
01101     return (m_launch == GALACTIC || m_launch == SPECGAL) && ( (current > max) && (z > 0) );
01102     
01103 }
01104 
01105 void FluxSource::correctForTiltAngle(){
01106     //Purpose: transform the incoming particle direction, correcting for the rocking angles of the satellite
01107     
01108     //get the transformation matrix..
01109     m_correctForTilt =GPS::instance()->rockingAngleTransform(GPS::instance()->time());
01110     m_correctedDir = m_correctForTilt*m_launchDir;
01111     //and return it.
01112     //return rockingAngles;
01113 }
01114 
01115 void FluxSource::spreadTheDirection(){
01116 double phi = RandFlat::shoot(M_2PI);
01117 double mcostheta = cos(m_degreespread*M_PI/180.);
01118 double costheta = RandFlat::shoot(mcostheta,1.0);
01119 Vector launchprime = m_launchDir;
01120 m_launchDir.rotate(acos(costheta) , m_launchDir.orthogonal());
01121 m_launchDir.rotate(phi,launchprime);
01122 
01123 }

Generated on Wed Oct 16 14:01:29 2002 by doxygen1.2.13.1 written by Dimitri van Heesch, © 1997-2001