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

HeSpectrum.cxx

Go to the documentation of this file.
00001 // $Id: HeSpectrum.cxx,v 1.7 2002/07/25 05:18:58 srobinsn Exp $
00002 
00003 #include "HeSpectrum.h"
00004 
00005 #include "HeSpectrum.inc"  // numerical data: energies, fluxes, gfluxes
00006 
00007 #include <cmath>
00008 #include <algorithm>
00009 #include <functional>
00010 #include "CLHEP/Random/Random.h"
00011 #include "GPS.h"
00012 
00013 #include "SpectrumFactory.h"
00014 
00015 static SpectrumFactory<HeSpectrum> factory;
00016 const ISpectrumFactory& HeSpectrumFactory = factory;
00017 
00018 
00019 
00020 // First deal with subclass HeSpectrum::InterpVec::
00021 //  which is a std::vector<float> with a couple of methods added for
00022 //  searching and interpolation.
00023 //-------------------------- InterpVec constructor
00024 
00025 HeSpectrum::InterpVec::InterpVec() : std::vector<float>() {}
00026 // default constructor.
00027 
00028 //-------------------------- InterpVec::search
00029 
00030 HeSpectrum::Intrp HeSpectrum::InterpVec::search(float x) const {
00031     using namespace std;
00032     // Purpose: Binary search a vector for values that straddle x using STL lower_bound.
00033     // Deal gracefully with values off the end by extrapolation.
00034     // Contents of vector must be monotonic.
00035     // Return integer index for use by another vector
00036     
00037     vector<float>::const_iterator loc;  // search. ascending or descending?
00038     if (back() > front()) loc=lower_bound(begin(), end(), x, less<float>());
00039     else                  loc=lower_bound(begin(), end(), x, greater<float>());
00040     // lower_bound points to the entry after x
00041     
00042     int ind;  // convert STL iterator to integer index
00043     if (loc == begin())    ind = 1;   // extrapolate below table
00044     else if (loc == end()) ind = (end() - begin()) - 1;  // above table
00045     else                   ind = (loc - begin());  // in the table
00046     
00047     return Intrp(ind, (x-(*this)[ind-1]) / ((*this)[ind]-(*this)[ind-1]));
00048 }
00049 
00050 //-------------------------- InterpVec::interpolate
00051 
00052 float HeSpectrum::InterpVec::interpolate(Intrp y) const {
00053     // linear interpolation between two std::vector values
00054     return (*this)[y.first-1] +
00055         y.second * ((*this)[y.first]-(*this)[y.first-1]);
00056 }
00057 
00058 //-------------------------- Beginning of HeSpectrum proper
00059 
00060 const float HeSpectrum::m_rearth = 6371.f;  // radius of earth in km
00061 const float HeSpectrum::m_altitude = 600.f;  // altitude of circular orbit
00062 
00063 
00064 void HeSpectrum::init(float lat, float lon) {
00065     //Purpose: Initializes parameters during construction
00066     int nen = sizeof(energies)/sizeof(float);
00067     m_en.reserve(nen);
00068     float amus = 4.;  // Mass of 4He; CHIME  energy is in MeV/amu
00069     int i;
00070     for (i=0; i< nen; i++) m_en.push_back(amus*energies[i]/1000.);
00071     // convert MeV to GeV
00072     
00073     m_normfact = .5*(1.+sqrt(m_altitude*(m_altitude+2.*m_rearth)) /
00074         (m_rearth+m_altitude));
00075     //geometrical shadow factor in 600 km orbit
00076     m_fluxes.reserve(nen);
00077     m_fl.reserve(nen);
00078     
00079     const float width = 0.115f; // CHIME log spacing between standard energies
00080     m_etop = m_en.back() * (1.+.5*width);  // boundary between table & tail
00081     m_expo = -2.65f + 1.0f;  // power law exponent (integral) of high-energy tail
00082     
00083     // Populate table of differential galactic fluxes -- no geomagnetic cutoff
00084     float tfl = 0.f;
00085     for (i=74; i >= 0; i--) {
00086         tfl += width*1000.*m_en[i]*m_normfact*fluxes[i];
00087         m_fluxes.insert(m_fluxes.begin(), tfl);
00088     }
00089     
00090     // table of total flux as a function of latitude and longitude
00091     for (int ii=0; ii < 73; ii++) {
00092         for (int jj=0; jj < 13; jj++) {
00093             m_fluxTbl[ii][jj] = gfluxes[jj+13*ii];
00094         }
00095     }
00096     setPosition(lat, lon);
00097     
00098     // cos(angle between zenith and horizon)
00099     m_coscutoff = -sqrt(m_altitude*m_altitude+2.*m_altitude*m_rearth)
00100         / (m_altitude+m_rearth);
00101     
00102     m_particle_name = "alpha";
00103     
00104     // set callback to be notified when the position changes
00105     m_observer.setAdapter( new ActionAdapter<HeSpectrum>(this,
00106         &HeSpectrum::askGPS) );
00107     
00108     GPS::instance()->notification().attach( &m_observer );
00109     
00110 }
00111 
00112 
00113 //-------------------------- constructors---------------------
00114 
00115 HeSpectrum::HeSpectrum(const std::string& paramstring) {
00116     std::vector<float> params;
00117     
00118     parseParamList(paramstring,params);
00119     
00120     if(params.empty())
00121         init(0.0f,0.0f);
00122     else
00123         init(params[0], params[1]);
00124 }
00125 
00126 std::string HeSpectrum::title() const {
00127     return "HeSpectrum";
00128 }
00129 
00130 const char * HeSpectrum::particleName() const {
00131     return m_particle_name.c_str();
00132 }
00133 void HeSpectrum::setParticleName(std::string name)
00134 {
00135     m_particle_name = name;
00136 }
00137 
00138 //-------------------------- flux()  (specified cutoff value)
00139 
00140 float HeSpectrum::flux(float cut) const {
00141     // Purpose:  Return total flux in nuclei / m^2 sec ster
00142     // Interpolate in table if possible, otherwise assume power law
00143     //  tail at high energy.
00144     if (cut > m_etop) return m_upper * pow(cut/m_etop, m_expo);
00145     else              return m_upper + m_fl.interpolate(m_en.search(cut));
00146 }
00147 
00148 //-------------------------- flux() (current cutoff value)
00149 
00150 double HeSpectrum::flux(double) const {
00151     // Purpose: calculate flux for the current cutoff
00152     return m_flux;
00153 }
00154 
00155 //--------------------------
00156 
00157 double HeSpectrum::solidAngle() const
00158 {
00159     // the normalization for the flux calculation
00160     return 4.*M_PI;
00161 }
00162 
00163 //-------------------------- flux() (specified position)
00164 
00165 double HeSpectrum::flux(double lat, double lon) const {
00166     // Purpose:  Return flux as a function of latitude and longitude in a 600 km orbit.
00167     // Linear interpolate in a table with a 5 degree sampling grid.
00168     int ilat = static_cast<int>(lat/5.+6.);
00169     double/*float*/ a = fmod(lat+30., 5.)/5.;
00170     int ilon = static_cast<int>(lon/5.);
00171     double/*float*/ b = fmod(lon, 5.)/5.;
00172     return m_fluxTbl[ilon][ilat] * (1.-a) * (1.-b) +
00173         m_fluxTbl[ilon+1][ilat] * (1.-a) * b +
00174         m_fluxTbl[ilon][ilat+1] * a * (1.-b) +
00175         m_fluxTbl[ilon+1][ilat+1] * a * b;
00176 }
00177 
00178 //-------------------------- operator()  sample an energy value
00179 
00180 float HeSpectrum::operator() (float x)const {
00181     // return a random value of energy sampled from the spectrum
00182     
00183     float join = (m_tot-m_upper)/m_tot;
00184     if (x < join) return m_en.interpolate(m_fl.search((1.-x)*m_tot-m_upper));
00185     else          return m_etop*pow((1.-x)/(1.-join), 1./m_expo);
00186 }
00187 
00188 //-------------------------- setPosition (separate coordinates)
00189 
00190 int HeSpectrum::askGPS()
00191 {
00192     setPosition(GPS::instance()->lat(), GPS::instance()->lon());
00193     return 0; // can't be void in observer pattern
00194 }
00195 
00196 void HeSpectrum::setPosition(double lat, double lon) {
00197     // Purpose:  Do the initialization necessary when moving to a new position:
00198     // look up cutoff energy, build a new table of integral alpha
00199     // fluxes
00200     
00201     m_lat = lat;
00202     m_lon = lon;
00203     
00204     // Integrated flux in the power law tail above the table.
00205     m_upper = -0.115*1000.*m_en.back()*m_normfact*fluxes[74]
00206         * pow(1.+.5*0.115,m_expo)/(0.115*m_expo);
00207     
00208     m_cutoff = findCutoff(lat,lon);
00209     
00210     // Populate table of integral fluxes modified by geomagnetic cutoff.
00211     float tfl = 0.;
00212     m_fl.erase(m_fl.begin(), m_fl.end());
00213     for (int i = 74; i >= 0; i--) {
00214         tfl += 0.115*1000.*m_en[i]*m_normfact*fluxes[i]*exposure(m_en[i]);
00215         m_fl.insert(m_fl.begin(), tfl);
00216     }
00217     
00218     m_tot = m_fl.front() + m_upper;
00219     m_flux = flux(m_cutoff);
00220 }
00221 
00222 //-------------------------- findCutoff (from a flux)
00223 
00224 float HeSpectrum::findCutoff(float rflux) const {
00225     // determine the cutoff value which will produce the desired flux
00226     return m_en.interpolate(m_fluxes.search(rflux-m_upper));
00227 }
00228 
00229 //-------------------------- findCutoff (from a position)
00230 
00231 float HeSpectrum::findCutoff(float lat, float lon) const {
00232     // determine the cutoff value at a geographical location
00233     return findCutoff(flux(lat,lon));
00234 }
00235 
00236 
00237 //------------------------- calculate_rate()
00238 
00239 double HeSpectrum::calculate_rate(double old_rate)
00240 {
00241     return flux(GPS::instance()->lat(), GPS::instance()->lon());
00242 }
00243 
00244 //------------------------- rad2()
00245 
00246 float HeSpectrum::rad2() const {
00247     // square of (distance from center of magnetic dipole / earth radius)
00248     // Dipole is offset from the earth's center
00249     
00250     /*float*/double d2 =
00251     pow((m_rearth+m_altitude)*sin(m_lat)            - 145.1, 2) +
00252         pow((m_rearth+m_altitude)*cos(m_lat)*cos(m_lon) + 371.2, 2) +
00253         pow((m_rearth+m_altitude)*cos(m_lat)*sin(m_lon) - 233.7, 2);
00254     return d2 / (m_rearth*m_rearth);
00255 }
00256 
00257 //------------------------- cosomega()
00258 
00259 float HeSpectrum::cosomega(float E) const {
00260     // Purpose:  Give opening angle of geomagnetic cutoff cone.
00261     // This is a pretty backward way to do it.  It starts from a cutoff
00262     // energy which is derived from a desired rate.  Then it calculates
00263     // the magnetic latitude from that.  Really, the cutoff and latitude
00264     // should be derived from the position by looking in tables.
00265     // Also, this is simple Størmer theory, ignoring  the penumbral
00266     // region and multipole effects.
00267     
00268     const float Mp = 4*0.938f;  // mass of alpha in GeV
00269     const float charge = 2.;    // electric charge of alpha
00270     float pcut = sqrt(m_cutoff*m_cutoff + 2.*Mp*m_cutoff);  // cutoff momentum
00271     const float moment = 59.8f; // magnetic moment of earth in convenient units
00272     float coslambda4 = 4. * pcut  * rad2() / (charge*moment);
00273     // magnetic latitude**4
00274     float p = sqrt(E*E + 2.*Mp*E); // momentum
00275     float coso = 4. * (sqrt(pcut/p) - pcut/p) * pow(coslambda4, -0.75);
00276     // opening angle of Størmer cone
00277     if (coso > 1.) return 1.;
00278     else if (coso < -1.) return -1.;
00279     else return coso;
00280 }
00281 
00282 //-------------------------- exposure()
00283 
00284 float HeSpectrum::exposure(float E) const {
00285     // Geomagnetic cutoff fraction.  Varies from 0 to 1.
00286     return 0.5 * (1. + cosomega(E));
00287 }
00288 
00289 //-------------------------- dir()
00290 
00291 std::pair<float,float> HeSpectrum::dir(float energy)const
00292 {
00293     
00294     // Purpose:  Random particle direction from Størmer cone
00295     
00296     // Rejection method for the direction.  Direction must be inside
00297     // the Stormer cone and above the horizon.
00298     float coszenith, sinpolar, cospolar, azi;
00299     const int try_max = 1000;
00300     static int max_tried = 0;
00301     int trial=0;
00302     do {
00303         // uniform distribution within Størmer cone
00304         cospolar = -1. + HepRandom::getTheGenerator()->flat()*
00305             (cosomega(energy)+1.);
00306         sinpolar = sqrt(1.-cospolar*cospolar);
00307         azi = 2.*M_PI* HepRandom::getTheGenerator()->flat();
00308         coszenith = cos(azi)*sinpolar;
00309     } while (coszenith < m_coscutoff && trial++ < try_max);
00310     
00311     max_tried = std::max(trial, max_tried); // keep track
00312     
00313     // Transform to local earth-based coordinates.
00314     /*float*/double earthazi = atan2(sinpolar*sin(azi), cospolar);
00315     
00316     return std::make_pair<float,float>(coszenith, earthazi);
00317 }

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