00001
00002
00003
00004 #include "flux/CHIMESpectrum.h"
00005
00006 #include "CHIMESpectrum.inc"
00007
00008 #include <cmath>
00009 #include <algorithm>
00010 #include <functional>
00011 #include "CLHEP/Random/Random.h"
00012 #include "flux/GPS.h"
00013
00014
00015 #include "SpectrumFactory.h"
00016
00017 static SpectrumFactory<CHIMESpectrum> factory;
00018 const ISpectrumFactory& CHIMESpectrumFactory = factory;
00019
00020
00021
00022
00023
00024
00025
00026 CHIMESpectrum::InterpVec::InterpVec() : std::vector<float>() {}
00027
00028
00029
00030
00031 CHIMESpectrum::Intrp CHIMESpectrum::InterpVec::search(float x) const {
00032
00033
00034
00035
00036
00037 std::vector<float>::const_iterator loc;
00038 if (back() > front()) loc=std::lower_bound(begin(), end(), x, std::less<float>());
00039 else loc=std::lower_bound(begin(), end(), x, std::greater<float>());
00040
00041
00042 int ind;
00043 if (loc == begin()) ind = 1;
00044 else if (loc == end()) ind = (end() - begin()) - 1;
00045 else ind = (loc - begin());
00046
00047 return Intrp(ind, (x-(*this)[ind-1]) / ((*this)[ind]-(*this)[ind-1]));
00048 }
00049
00050
00051
00052 float CHIMESpectrum::InterpVec::interpolate(Intrp y) const {
00053
00054 return (*this)[y.first-1] +
00055 y.second * ((*this)[y.first]-(*this)[y.first-1]);
00056 }
00057
00058
00059
00060 const float CHIMESpectrum::m_rearth = 6371.f;
00061 const float CHIMESpectrum::m_altitude = 600.f;
00062
00063
00064 void CHIMESpectrum::init(std::string paramstring) {
00065
00066
00067 std::vector<float> params;
00068
00069 parseParamList(paramstring,params);
00070
00071 int nen = sizeof(energies)/sizeof(float);
00072 m_en.reserve(nen);
00073 int i;
00074 for (i=0; i< nen; i++) m_en.push_back(energies[i]/1000.);
00075
00076 m_normfact = .5*(1.+sqrt(m_altitude*(m_altitude+2.*m_rearth)) /
00077 (m_rearth+m_altitude));
00078
00079 m_fluxes.reserve(nen);
00080 m_fl.reserve(nen);
00081
00082 const float width = 0.115f;
00083 m_etop = m_en.back() * (1.+.5*width);
00084 m_expo = -2.75 + 1.0;
00085
00086
00087 float tfl = 0.f;
00088 for (i=74; i >= 0; i--) {
00089 tfl += width*1000.*m_en[i]*m_normfact*fluxes[i];
00090 m_fluxes.insert(m_fluxes.begin(), tfl);
00091 }
00092
00093
00094 for (int ii=0; ii < 73; ii++) {
00095 for (int jj=0; jj < 13; jj++) {
00096 m_fluxTbl[ii][jj] = gfluxes[jj+13*ii];
00097 }
00098 }
00099 double lat = params.size()>0? params[0]: 0.0f;
00100 double lon = params.size()>1? params[1]: 0.0f;
00101
00102 setPosition(lat, lon);
00103
00104
00105 m_coscutoff = -sqrt(m_altitude*m_altitude+2.*m_altitude*m_rearth)
00106 / (m_altitude+m_rearth);
00107
00108 m_particle_name = "p";
00109
00110
00111 m_observer.setAdapter( new ActionAdapter<CHIMESpectrum>(this,&CHIMESpectrum::askGPS) );
00112
00113 GPS::instance()->notification().attach( &m_observer );
00114
00115 }
00116
00117
00118
00119
00120 CHIMESpectrum::CHIMESpectrum(const std::string& params) {
00121 init(params);
00122 }
00123
00124
00125 std::string CHIMESpectrum::title() const {
00126 return "CHIMESpectrum";
00127 }
00128
00129 const char * CHIMESpectrum::particleName() const {
00130 return m_particle_name.c_str();
00131 }
00132 void CHIMESpectrum::setParticleName(std::string name)
00133 {
00134 m_particle_name = name;
00135 }
00136
00137
00138
00139 float CHIMESpectrum::flux(float cut) const {
00140
00141
00142
00143 if (cut > m_etop) return m_upper * pow(cut/m_etop, m_expo);
00144 else return m_upper + m_fl.interpolate(m_en.search(cut));
00145 }
00146
00147
00148
00149 double CHIMESpectrum::flux() const {
00150
00151 return m_flux;
00152 }
00153
00154
00155
00156 double CHIMESpectrum::solidAngle() const
00157 {
00158
00159 return 4.* M_PI;
00160 }
00161
00162
00163
00164 float CHIMESpectrum::flux(float lat, float lon) const {
00165
00166
00167 int ilat = static_cast<int>(lat/5.+6.);
00168 double a = fmod(lat+30., 5.)/5.;
00169 int ilon = static_cast<int>(lon/5.);
00170 double b = fmod(lon, 5.)/5.;
00171 return m_fluxTbl[ilon][ilat] * (1.-a) * (1.-b) +
00172 m_fluxTbl[ilon+1][ilat] * (1.-a) * b +
00173 m_fluxTbl[ilon][ilat+1] * a * (1.-b) +
00174 m_fluxTbl[ilon+1][ilat+1] * a * b;
00175 }
00176
00177
00178
00179 float CHIMESpectrum::flux(std::pair<double,double> coords) const {
00180
00181 return flux(coords.first, coords.second);
00182 }
00183
00184
00185
00186 float CHIMESpectrum::operator() (float x)const {
00187
00188
00189 float join = (m_tot-m_upper)/m_tot;
00190 if (x < join) return m_en.interpolate(m_fl.search((1.-x)*m_tot-m_upper));
00191 else return m_etop*pow((1.-x)/(1.-join), 1./m_expo);
00192 }
00193
00194
00195
00196 int CHIMESpectrum::askGPS()
00197 {
00198 setPosition(GPS::instance()->lat(), GPS::instance()->lon());
00199 return 0;
00200 }
00201
00202 void CHIMESpectrum::setPosition(double lat, double lon) {
00203
00204
00205
00206
00207 m_lat = lat;
00208 m_lon = lon;
00209
00210
00211 m_upper = -0.115*1000.*m_en.back()*m_normfact*fluxes[74]
00212 * pow(1.+.5*0.115,m_expo)/(0.115*m_expo);
00213
00214 m_cutoff = findCutoff(lat,lon);
00215
00216
00217 float tfl = 0.;
00218 m_fl.erase(m_fl.begin(), m_fl.end());
00219 for (int i = 74; i >= 0; i--) {
00220 tfl += 0.115*1000.*m_en[i]*m_normfact*fluxes[i]*exposure(m_en[i]);
00221 m_fl.insert(m_fl.begin(), tfl);
00222 }
00223
00224 m_tot = m_fl.front() + m_upper;
00225 m_flux = flux(m_cutoff);
00226 }
00227
00228
00229
00230 void CHIMESpectrum::setPosition(std::pair<double,double> coords) {
00231 CHIMESpectrum::setPosition(coords.first, coords.second);
00232 }
00233
00234
00235
00236 float CHIMESpectrum::findCutoff(float rflux) const {
00237
00238 return m_en.interpolate(m_fluxes.search(rflux-m_upper));
00239 }
00240
00241
00242
00243 float CHIMESpectrum::findCutoff(float lat, float lon) const {
00244
00245 return findCutoff(flux(lat,lon));
00246 }
00247
00248
00249
00250 float CHIMESpectrum::findCutoff(std::pair<double,double> coords) const {
00251
00252 return findCutoff(flux(coords));
00253 }
00254
00255
00256
00257 double CHIMESpectrum::calculate_rate(double old_rate)
00258 {
00259 return flux(GPS::instance()->lat(), GPS::instance()->lon());
00260 }
00261
00262
00263
00264 float CHIMESpectrum::rad2() const {
00265
00266
00267
00268 double d2 =
00269 pow((m_rearth+m_altitude)*sin(m_lat) - 145.1, 2) +
00270 pow((m_rearth+m_altitude)*cos(m_lat)*cos(m_lon) + 371.2, 2) +
00271 pow((m_rearth+m_altitude)*cos(m_lat)*sin(m_lon) - 233.7, 2);
00272 return d2 / (m_rearth*m_rearth);
00273 }
00274
00275
00276
00277 float CHIMESpectrum::cosomega(float E) const {
00278
00279
00280
00281
00282
00283
00284
00285
00286 const float Mp = 0.938f;
00287 double pcut = sqrt(m_cutoff*m_cutoff + 2.*Mp*m_cutoff);
00288 const float moment = 59.8f;
00289 double coslambda4 = 4. * pcut * rad2() / moment;
00290 double p = sqrt(E*E + 2.*Mp*E);
00291 double coso = 4. * (sqrt(pcut/p) - pcut/p) * pow(coslambda4, -0.75);
00292
00293 if (coso > 1.) return 1.;
00294 else if (coso < -1.) return -1.;
00295 else return coso;
00296 }
00297
00298
00299
00300 float CHIMESpectrum::exposure(float E) const {
00301
00302 return 0.5 * (1. + cosomega(E));
00303 }
00304
00305
00306
00307 std::pair<float,float> CHIMESpectrum::dir(float energy)const
00308 {
00309
00310
00311
00312
00313
00314 double coszenith, sinpolar, cospolar, azi;
00315 const int try_max = 1000;
00316 static int max_tried = 0;
00317 int trial=0;
00318 do {
00319
00320 cospolar = -1. + HepRandom::getTheGenerator()->flat()*(cosomega(energy)+1.);
00321 sinpolar = sqrt(1.-cospolar*cospolar);
00322 azi = 2.*M_PI* HepRandom::getTheGenerator()->flat();
00323 coszenith = cos(azi)*sinpolar;
00324 } while (coszenith < m_coscutoff && trial++ < try_max);
00325
00326 max_tried = std::max(trial, max_tried);
00327
00328
00329 float earthazi = atan2(sinpolar*sin(azi), cospolar);
00330
00331 return std::make_pair<float,float>(coszenith, earthazi);
00332 }