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

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

Generated at Wed Nov 21 12:20:34 2001 by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000