earthmodel.cc
1 #include "Constants.h"
2 #include "Settings.h"
3 #include "earthmodel.hh"
4 #include "icemodel.hh"
5 #include <cmath>
6 #include "Tools.h"
7 #include "vector.hh"
8 #include "position.hh"
9 #include <iostream>
10 #include <fstream>
11 #include "icemc_random.h"
12 
13 #include "signal.hh"
14 #include "Primaries.h"
15 #include "secondaries.hh"
16 #include "EnvironmentVariable.h"
17 
18 const string ICEMC_SRC_DIR=EnvironmentVariable::ICEMC_SRC_DIR();
19 const string ICEMC_DATA_DIR=ICEMC_SRC_DIR+"/data/";
20 // input files for Crust 2.0
21 const string crust20_in=ICEMC_DATA_DIR+"/outcr"; // Crust 2.0 data
22 const string crust20_out=ICEMC_SRC_DIR+"/altitudes.txt"; // output file for plotting
23 
24 
25 using std::cout;
26 using std::endl;
27 using std::ios;
28 using std::fstream;
29 
30 
31 const double EarthModel::COASTLINE(30.);
32 const double EarthModel::MAXTHETA(180.);
33 const int EarthModel::ILAT_COASTLINE((int)((COASTLINE/MAXTHETA)*(double)NLAT+0.00001)); // corresponding latitude bin to "coastline"
34 const double EarthModel::GEOID_MAX(6.378137E6); // parameters of geoid model
35 const double EarthModel::GEOID_MIN(6.356752E6); // from Geodetic Reference System 1980, Bulletin Geodesique, Vol 54:395,1980. // The previous reference gave issue number 3 instead of page number 395
36 
37 
38 EarthModel::EarthModel(int model,int WEIGHTABSORPTION_SETTING) {
39 
40 
41  FLATSURFACE = 0;
42  radii[0]=1.2e13;
43  radii[1]=(EarthModel::R_EARTH-4.0E4)*(EarthModel::R_EARTH-4.0E4);
44  radii[2]=(EarthModel::R_EARTH*EarthModel::R_EARTH); // average radii of boundaries between earth layers
45 
46 
47  // cout << "In EarthModel, model is " << model << "\n";
48  weightabsorption= WEIGHTABSORPTION_SETTING;
49 
50  CONSTANTICETHICKNESS = (int) (model / 1000);
51  model -= CONSTANTICETHICKNESS * 1000;
52 
53  CONSTANTCRUST = (int) (model / 100);
54  model -= CONSTANTCRUST * 100;
55 
56  FIXEDELEVATION = (int) (model / 10);
57  model -= FIXEDELEVATION * 10;
58 
59  EARTH_MODEL = model;
60  //cout<<"CONSTICETHK = "<<CONSTANTICETHICKNESS<<", CNSTCRST = "<<CONSTANTCRUST<<", FIXDELV = "<<FIXEDELEVATION<<", EARTH_MODEL = "<<EARTH_MODEL<<endl;
61  for (int i=0;i<NLON;i++) {
62 
63  Tools::Zero(elevationarray[i],NLAT);
64  Tools::Zero(waterthkarray[i],NLAT);
65  Tools::Zero(icethkarray[i],NLAT);
66  Tools::Zero(softsedthkarray[i],NLAT);
67  Tools::Zero(hardsedthkarray[i],NLAT);
68  Tools::Zero(uppercrustthkarray[i],NLAT);
69  Tools::Zero(middlecrustthkarray[i],NLAT);
70  Tools::Zero(lowercrustthkarray[i],NLAT);
71  Tools::Zero(crustthkarray[i],NLAT);
72 
73 
74  Tools::Zero(surfacer[i],NLAT);
75  Tools::Zero(icer[i],NLAT);
76  Tools::Zero(waterr[i],NLAT);
77  Tools::Zero(softsedr[i],NLAT);
78  Tools::Zero(hardsedr[i],NLAT);
79  Tools::Zero(uppercrustr[i],NLAT);
80  Tools::Zero(middlecrustr[i],NLAT);
81  Tools::Zero(lowercrustr[i],NLAT);
82 
83  Tools::Zero(waterdensityarray[i],NLAT);
84  Tools::Zero(icedensityarray[i],NLAT);
85  Tools::Zero(softseddensityarray[i],NLAT);
86  Tools::Zero(hardseddensityarray[i],NLAT);
87  Tools::Zero(uppercrustdensityarray[i],NLAT);
88  Tools::Zero(middlecrustdensityarray[i],NLAT);
89  Tools::Zero(lowercrustdensityarray[i],NLAT);
90 
91  } //Zero Earth model arrays
92 
93  // see monte carlo note #17
94  for (int i=0;i<NLAT;i++) {
95  geoid[i]=GEOID_MIN*GEOID_MAX/sqrt(pow(GEOID_MIN,2.)-(pow(GEOID_MIN,2.)-pow(GEOID_MAX,2.))*pow(cos(dGetTheta(i)),2.));
96  } //for
97 
98 
99  // Crust 2.0 is binned in 2deg x 2deg bins, area of bin depends on latitude.
100  // calculating surface area of bins
101  phistep=2*PI/(double)NLON;
102  thetastep=(MAXTHETA*RADDEG)/NLAT;
103  for (int i=0;i<NLAT;i++) {
104  area[i]=phistep*(cos(dGetTheta(i))-cos(dGetTheta(i+1)))*pow(geoid[i],2.);
105  } //for
106 
107  if (EARTH_MODEL == 0)
108  ReadCrust(crust20_in);
109  else {
110  cout<<"Error! Unknown Earth model requested! Defaulting to Crust 2.0 model.\n";
111  ReadCrust(crust20_in);
112  } //else
113 
114 } //EarthModel constructor (int mode)
115 
116 EarthModel::~EarthModel() {} //EarthModel destructor - no dynamic variables, nothing to delete
117 
118 
119 double EarthModel::LongtoPhi_0isPrimeMeridian(double longitude) {
120 
121  double phi;
122  // convert longitude (-180 to 180) to phi (0 to 2pi) wrt +x
123  // in radians
124  phi=(90-longitude);
125  if (phi<0.)
126  phi+=360.;
127 
128  phi=phi*RADDEG;
129 
130  return phi;
131 }
132 double EarthModel::LongtoPhi_0is180thMeridian(double longitude) {
133 
134  double phi;
135  // convert longitude (0 to 360) to phi (0 to 2pi) wrt +x
136 
137  phi=(270.-longitude);
138  if (phi<0.)
139  phi+=360.;
140 
141  phi=phi*RADDEG;
142  // in radians
143 
144  return phi;
145 }
146 
147 double EarthModel::Geoid(double latitude) {
148  // latitude here is 0 at the south pole and 180 at the north pole
149 
150  return (GEOID_MIN*GEOID_MAX/
151  sqrt(GEOID_MIN*GEOID_MIN-(GEOID_MIN*GEOID_MIN-GEOID_MAX*GEOID_MAX)*
152  cos(latitude*RADDEG)*cos(latitude*RADDEG)));
153 } //Geoid(lat)
154 
155 double EarthModel::Geoid(const Position &pos) {
156  return Geoid(pos.Lat());
157 } //Geoid(Position)
158 
159 double EarthModel::IceThickness(double lon,double lat) {
160  return icethkarray[(int)(lon/2)][(int)(lat/2)]*1000.;
161 } //IceThickness(lon,lat)
162 
163 double EarthModel::IceThickness(const Position& pos) {
164  return IceThickness(pos.Lon(),pos.Lat());
165 } //IceThickness(Position)
166 int EarthModel::InFirn(const Position& pos) {
167  if (pos.Mag()-Surface(pos)<FIRNDEPTH)
168  return 0;
169  return 1;
170 } //InFirn(Position)
171 double EarthModel::SurfaceDeepIce(const Position& pos) { // surface of the deep ice (where you reach the firn)
172  return surfacer[(int)(pos.Lon()/2)][(int)(pos.Lat()/2)] + geoid[(int)(pos.Lat()/2)] + FIRNDEPTH;
173 } //Surface(lon,lat)
174 
175 double EarthModel::Surface(double lon,double lat) {
176  return surfacer[(int)(lon/2)][(int)(lat/2)] + geoid[(int)(lat/2)];
177 } //Surface(lon,lat)
178 
179 double EarthModel::Surface(const Position& pos) {
180  return surfacer[(int)(pos.Lon()/2)][(int)(pos.Lat()/2)] + geoid[(int)(pos.Lat()/2)];
181 } //Surface(Position)
182 
183 double EarthModel::RockSurface(double lon,double lat) {
184  return (Surface(lon,lat) - IceThickness(lon,lat) - WaterDepth(lon,lat));
185 } //RockSurface(lon,lat)
186 
187 double EarthModel::RockSurface(const Position& pos) {
188  return RockSurface(pos.Lon(),pos.Lat());
189 } //RockSurface(lon,lat)
190 
191 double EarthModel::SurfaceAboveGeoid(double lon,double lat) {
192  return surfacer[(int)(lon/2)][(int)(lat/2)];
193 } //SurfaceAboveGeoid(lon,lat)
194 
195 double EarthModel::SurfaceAboveGeoid(const Position& pos) {
196  return surfacer[(int)(pos.Lon()/2)][(int)(pos.Lat()/2)];
197 } //SurfaceAboveGeoid(Position)
198 
199 double EarthModel::WaterDepth(double lon,double lat) {
200  return waterthkarray[(int)(lon/2)][(int)(lat/2)]*1000;
201 } //WaterDepth(lon,lat)
202 
203 double EarthModel::WaterDepth(const Position& pos) {
204  return WaterDepth(pos.Lon(),pos.Lat());
205 } //WaterDepth(Position)
206 
207 double EarthModel::GetLat(double theta) {
208  return theta*DEGRAD;
209 } //GetLat
210 
211 double EarthModel::GetLon(double phi) {
212  // input is phi in radians wrt +x
213  double phi_deg = phi*DEGRAD;
214  if (phi_deg > 270)
215  phi_deg = phi_deg - 360.; // this puts it from -90 to 270
216 
217  return (360.*3./4. - phi_deg); // returns 0 to 360 degrees (going from -180 to 180 deg longitude like Crust 2.0 does)
218 } //GetLon
219 
220 double EarthModel::GetDensity(double altitude, const Position earth_in,
221  int& crust_entered, // 1 or 0
222  bool * inice
223  ){
224 
225  Position where = earth_in;
226 
227  double lon = where.Lon();
228  double lat = where.Lat();
229  //cout <<"Lon and Lat are "<<lon<<","<<lat<<"\n";
230 
231  int ilon = (int)(lon/2);
232  int ilat = (int)(lat/2);
233 
234  double ddensity =0; //initilize ddensity
235 
236  double surface_elevation = this->SurfaceAboveGeoid(lon,lat); // altitude of surface relative to geoid at earth entrance point
237 
238  double local_icethickness = this->IceThickness(lon,lat);
239  double local_waterdepth = WaterDepth(lon,lat);
240 
241  if (inice) *inice = false;
242 
243 
244  if(altitude>surface_elevation+0.1){ // if it is above the surface, it's messed up
245 
246  }
247  if(altitude>surface_elevation+0.1){
248  ddensity=1.25;
249  //cout <<"density is air! \n";
250  }
251  if (altitude<=surface_elevation+0.1 && altitude>(surface_elevation-local_icethickness)) // the 0.1 is just to take care of precision issues. It could have been 0.01 or 0.001.
252  {
253  ddensity=icedensityarray[ilon][ilat]*1000;
254  if (inice) *inice = true;
255  }
256 
257  else if (altitude<=(surface_elevation-local_icethickness) && altitude>(surface_elevation-local_icethickness-local_waterdepth))
258  ddensity=waterdensityarray[ilon][ilat]*1000;
259  else if (altitude<=(surface_elevation-local_icethickness-local_waterdepth) && altitude>softsedr[ilon][ilat]) {
260  ddensity=softseddensityarray[ilon][ilat]*1000;
261  crust_entered=1; //Switch that lets us know we've penetrated into the crust
262  } //end if
263  else if (altitude<=softsedr[ilon][ilat] && altitude>hardsedr[ilon][ilat])
264  ddensity=hardseddensityarray[ilon][ilat]*1000;
265  else if (altitude<=hardsedr[ilon][ilat] && altitude>uppercrustr[ilon][ilat])
266  ddensity=uppercrustdensityarray[ilon][ilat]*1000;
267  else if (altitude<=uppercrustr[ilon][ilat] && altitude>middlecrustr[ilon][ilat])
268  ddensity=middlecrustdensityarray[ilon][ilat]*1000;
269  else if (altitude<=middlecrustr[ilon][ilat] && altitude>lowercrustr[ilon][ilat])
270  ddensity=lowercrustdensityarray[ilon][ilat]*1000;
271  else if (altitude<=lowercrustr[ilon][ilat])
272  ddensity=densities[1];
273 
274  return ddensity;
275 
276 }//Get Density
277 
278 
279 
280 int EarthModel::Getchord(Settings *settings1,
281  double len_int_kgm2,
282  const Position &earth_in, // place where neutrino entered the earth
283  double distance_in_ice,
284  bool include_ice_absorption,
285  const Position &posnu, // position of the interaction
286  int inu,
287  double& chord, // chord length
288  double& probability_tmp, // weight
289  double& weight1_tmp,
290  double& nearthlayers, // core, mantle, crust
291  double myair,
292  double& total_kgm2, // length in kg m^2
293  int& crust_entered, // 1 or 0
294  int& mantle_entered, // 1 or 0
295  int& core_entered) {
296 
297  Vector chord3;
298  Vector nchord;
299  double x=0;
300  double lat,lon;
301  // int ilon,ilat;
302 
303  total_kgm2 = 0; //Initialize column density
304  nearthlayers=0; // this counts the number of earth layers the neutrino traverses.
305  // Want to find probability that the neutrino survives its trip
306  // through the earth.
307 
308  //Find the chord, its length and its unit vector.
309  chord3 = posnu - earth_in;
310  chord=chord3.Mag();
311  nchord = chord3 / chord;
312 
313  if (chord<=1) {
314  cout << "short chord " << chord << "\n";
315  return 0;
316  }
317  if (chord>2.*R_EARTH+1000) {
318  cout << "bad chord" << " " << chord << ". Event is " << inu << "\n";
319  }
320 
321  Position where=earth_in;
322  //cout <<"where(1) is "<<where;
323  // the sin of the angle between the neutrino path and the
324  // radial vector to its earth entrance point determines
325  // if it will get to the next layer down.
326  double costh=(where*nchord)/where.Mag();
327  double sinth=sqrt(1-costh*costh);
328  double distance=0;
329  double halfchord=0;
330 
331 
332 
333  if (getchord_method<1 || getchord_method>3)
334  cout << "Bogus method!\n";
335 
336  // we are really focusing on method 2 - method 1 has not been maintenanced in a long time!!
337  // use at your own risk.
338  if (getchord_method==1) {
339  double L=0;
340  weight1_tmp=0;
341 
342  if (sinth>sqrt(radii[1]/radii[2])) {
343  nearthlayers++;
344 
345  // these only skim the first layer.
346  L=len_int_kgm2/densities[2];
347 
348  weight1_tmp=exp(-posnu.Distance(where)/L);
349  }
350  else {
351  nearthlayers++;
352 
353  // these get to the second layer down.
354  L=len_int_kgm2/densities[2];
355  // compute distance before it gets to the next layer.
356  halfchord=sqrt(radii[1]-radii[2]*sinth*sinth);
357  distance=sqrt(radii[2])*costh-halfchord;
358 
359  weight1_tmp=exp(-distance/L);
360 
361  // get position where it enters the second layer.
362  where = earth_in + distance*nchord;
363 
364  // determine if it enters the core or not.
365  costh=(where*nchord)/where.Mag();
366  sinth=sqrt(1-costh*costh);
367 
368  if (sinth>sqrt(radii[0]/radii[1])) {
369 
370 
371  halfchord=sqrt(radii[1])*costh;
372  nearthlayers++;
373 
374  // these do not enter the core.
375  L=len_int_kgm2/densities[1];
376 
377 
378  weight1_tmp *= exp(-2*halfchord/L);
379 
380  L=len_int_kgm2/densities[2];
381  // this is where it exits the second layer and enters the crust again.
382  where = where + 2*halfchord*nchord;
383  weight1_tmp*=exp(-where.Distance(posnu)/L);
384  }
385  else {
386  nearthlayers++;
387  // these enter the core.
388  L=len_int_kgm2/densities[1];
389 
390  // compute distance before entering the core.
391  halfchord=sqrt(radii[0]-radii[1]*sinth*sinth);
392  distance=sqrt(radii[1])*costh-halfchord;
393  weight1_tmp*=exp(-distance/L);
394 
395  // go through the core.
396  L=len_int_kgm2/densities[0];
397  weight1_tmp*=exp(-2*halfchord/L);
398 
399  // go through 2nd layer again.
400  L=len_int_kgm2/densities[1];
401  weight1_tmp*=exp(-distance/L);
402 
403  // through the crust and end up at posnu.
404  L=len_int_kgm2/densities[2];
405 
406  where = where + (2*distance+2*halfchord)*nchord;
407  weight1_tmp*=exp(-where.Distance(posnu)/L);
408  } //else
409  } //else
410  } //if getchord_method==1
411  if (getchord_method==2) {
412 
413  x=0; // x is the distance you move as you step through the earth.
414 
415  lon = where.Lon();
416  lat = where.Lat();
417  // ilon = (int)(lon/2);
418  // ilat = (int)(lat/2);
419 
420  double surface_elevation = this->SurfaceAboveGeoid(lon,lat); // altitude of surface relative to geoid at earth entrance point
421 
422  // double local_icethickness = this->IceThickness(lon,lat);
423  // double local_waterdepth = WaterDepth(lon,lat);
424  double altitude=0;
425  weight1_tmp=1;
426  probability_tmp=1;
427  double step=Tools::dMin(len_int_kgm2/densities[1]/10,500.); //how big is the step size
428  // either 1/10 of an interaction length in the mantle or 500 m, whichever is smaller.
429  // 500 m is approximately the feature size in Crust 2.0.
430  //------------------added on Dec 8------------------------
431  weight1_tmp*=exp(-myair/len_int_kgm2);//add atmosphere attenuation // fenfang's atten. due to atmosphere
432  //------------------added on Dec 8------------------------
433  total_kgm2+=myair;
434 
435 
436 
437  double L_ice=len_int_kgm2/Signal::RHOICE;
438 
439  if (settings1->UNBIASED_SELECTION)
440  probability_tmp*=1-exp(-1.*(distance_in_ice/L_ice)); // probability it interacts somewhere along its path (at all). We have already sampled exponentially along the chord, so want to multiply by chance of interacting at all!
441 
442  double L=0;
443 
444  double ddensity=Signal::RHOAIR;
445  nearthlayers=1;
446 
447  if (where*nchord>0.) { // look at direction of neutrino where it enters the earth.
448  cout << "This one's trouble. Neutrino exit point looks more like an entrance point. Event is " << inu << "\n";
449  cout << "where is " << where[0] << " " << where[1] << " " << where[2] << "\n";
450  cout << "nchord is " << nchord[0] << " " << nchord[1] << " " << nchord[2] << "\n";
451  cout << "dot product is " << where*nchord/sqrt(where*where) << "\n";
452  cout << "posnu is " << posnu[0] << " " << posnu[1] << " " << posnu[2] << "\n";
453  cout << "Length of chord is : "<<chord<<endl;
454  } //end if
455 
456  altitude=where.Mag()-Geoid(lat); // what is the altitude of the entrance point
457 
458  if(altitude>surface_elevation+0.1) // if it is above the surface, it's messed up
459  cout << "neutrino entrance point is above the surface. Event is " << inu << "\n";
460  //cout <<"altitude is "<<altitude<<"\n";
461 
462  while(altitude>MIN_ALTITUDE_CRUST && x<posnu.Distance(earth_in)) { // starting at earth entrance point, step toward interaction position until you've reached the interaction or you are below the crust.
463  // while(altitude>MIN_ALTITUDE_CRUST && x<dDistance(enterice,earth_in)) {
464 
465  bool inice;
466  ddensity = this->GetDensity(altitude,where,crust_entered,&inice);
467 
468  L=len_int_kgm2/ddensity; // get the interaction length for that density
469  if (!inice || include_ice_absorption)
470  {
471  weight1_tmp*=exp(-step/L); // adjust the weight accordingly
472  total_kgm2+=ddensity*step; //increase column density accordingly
473  }
474  if (exp(-step/L) > 1)
475  cout<<"Oops! len_int_kgm2, ddensity, factor : "<<len_int_kgm2<<" , "<<ddensity<<" , "<<exp(-step/L)<<endl;
476  x+=step; // distance you have stepped through the earth so far.
477 
478  where += step*nchord;// find where you are now along the neutrino's path
479 
480  lon = where.Lon();
481  lat = where.Lat();
482  // ilon = (int)(lon/2);
483  // ilat = (int)(lat/2);
484  altitude=where.Mag()-Geoid(lat); //what is the altitude
485  surface_elevation = this->SurfaceAboveGeoid(lon,lat); // altitude of surface relative to geoid at earth entrance point
486  // local_icethickness = this->IceThickness(lon,lat);
487  // local_waterdepth = WaterDepth(lon,lat);
488 
489  } //end while
490 
491  if (x>posnu.Distance(earth_in) && weightabsorption) { // if you left the loop because you have already stepped the whole distance from the entrance point to the neutrino interaction position
492  probability_tmp*=weight1_tmp;
493  return 1;
494  }
495  // if you left the loop because you're not in the crust anymore
496  if (altitude<=MIN_ALTITUDE_CRUST) {
497 
498  mantle_entered=1; //Switch that lets us know we're into the mantle
499 
500  // determine if it enters the core or not.
501  sinth=sin(where.Angle(nchord));
502  costh=sqrt(1-sinth*sinth);
503 
504  if (sinth>sqrt(radii[0]/radii[1])) { // it does not enter the core, just the mantle
505 
506  nearthlayers++; // count the mantle as a layer traversed.
507 
508  L=len_int_kgm2/densities[1]; // interaction length in the mantle
509  halfchord=sqrt(radii[1])*costh; // 1/2 chord the neutrino goes through in the mantle
510 
511  weight1_tmp *= exp(-2*halfchord/L); // adjust the weight for path through mantle
512  total_kgm2+= 2*halfchord*densities[1]; //add column density for path through mantle
513  where += (2*halfchord)*nchord; // neutrino's new position once it reaches the crust again
514 
515  } //end if (not entering core)
516  // these enter the core
517  else {
518  core_entered=1; //Switch that lets us know we've entered the core
519 
520  nearthlayers+=2; // count the mantle and core as a layer traversed.
521 
522  L=len_int_kgm2/densities[1]; // interaction length in mantle
523 
524  // compute distance before entering the core.
525  halfchord=sqrt(radii[0]-radii[1]*sinth*sinth); // find distance it travels in the mantle
526  distance=sqrt(radii[1])*costh-halfchord;
527  weight1_tmp*=exp(-distance/L); // adjust the weight
528  total_kgm2 += 2*distance*densities[1]; //Add column density for trip through mantle
529  // go through the core.
530  L=len_int_kgm2/densities[0]; // interaction length in core
531  weight1_tmp*=exp(-2*halfchord/L); // adjust the weight
532  total_kgm2 += 2*halfchord*densities[0]; //Add column density for trip through core
533  // go through 2nd layer again.
534  L=len_int_kgm2/densities[1];
535  weight1_tmp*=exp(-distance/L);
536  if (exp(-distance/L) > 1)
537  cout<<"Oops2! len_int_kgm2, ddensity, distance, factor : "<<len_int_kgm2<<" , "<<ddensity<<" , "<<distance<<" , "<<exp(-distance/L)<<endl;
538  where += (2*distance+2*halfchord)*nchord; // neutrino's new position once it reaches the crust again
539 
540  } //end else(enter core)
541  } //end if(left crust)
542 
543  lon = where.Lon();
544  lat = where.Lat();
545  // ilon = (int)(lon/2);
546  // ilat = (int)(lat/2);
547  altitude=where.Mag()-Geoid(lat); //what is the altitude
548  surface_elevation = this->SurfaceAboveGeoid(lon,lat); // altitude of surface relative to geoid at earth entrance point
549  // local_icethickness = this->IceThickness(lon,lat);
550  // local_waterdepth = WaterDepth(lon,lat);
551 
552  double distance_remaining=where.Distance(posnu); // how much farther you need to travel before you reach the neutrino interaction point
553 
554  x=0; // this keeps track of how far you've stepped along the neutrino path, starting at the crust entrance.
555  while(x<=distance_remaining) { // keep going until you have reached the interaction position
556 
557  bool inice;
558  ddensity=this->GetDensity(altitude,where,crust_entered,&inice);
559 
560  L=len_int_kgm2/ddensity; // get the interaction length for that density
561 
562  if (!inice || include_ice_absorption)
563  {
564  weight1_tmp*=exp(-step/L); // adjust the weight accordingly
565  total_kgm2 += step*ddensity;
566  }
567 
568  if (exp(-step/L) > 1)
569  cout<<"Oops3! len_int_kgm2, ddensity, step, factor : "<<len_int_kgm2<<" , "<<ddensity<<" , "<<step<<" , "<<exp(-step/L)<<endl;
570  x+=step; // increment how far you've stepped through crust
571 
572 
573  // possible for a neutrino to go through the air but not likely because they aren't the most extreme skimmers (they went through the mantle)
574  where += step*nchord; // where you are now along neutrino's path
575 
576  lon = where.Lon();
577  lat = where.Lat();
578  // ilon = (int)(lon/2);
579  // ilat = (int)(lat/2);
580  altitude=where.Mag()-Geoid(lat); //what is the altitude
581  surface_elevation = this->SurfaceAboveGeoid(lon,lat); // altitude of surface relative to geoid at earth entrance point
582  // local_icethickness = this->IceThickness(lon,lat);
583  // local_waterdepth = WaterDepth(lon,lat);
584  } //while
585 
586  } //if (getchord_method == 2)
587 
588  probability_tmp*=weight1_tmp;
589  //cout <<"probability_tmp(non-tau) is "<<probability_tmp<<".\n";
590 
591  if (weightabsorption==0) {
592  if (getRNG(RNG_ABSORB)->Rndm()>weight1_tmp) {
593 
594  weight1_tmp=0.;
595  return 0;
596  }
597  else {
598 
599  weight1_tmp=1.;
600  return 1;
601  }
602  }
603  else
604  return 1;
605 
606  cout << "made it this far.\n";
607 
608  return 1;
609 } //end Getchord
610 
611 Vector EarthModel::GetSurfaceNormal(const Position &r_out)
612 {
613  Vector n_surf = r_out.Unit();
614  if (FLATSURFACE)
615  return n_surf;
616 
617  double theta=r_out.Theta();
618 
619  int ilon,ilat;
620  GetILonILat(r_out,ilon,ilat);
621 
622  int ilon_previous=ilon-1;
623  if (ilon_previous<0)
624  ilon_previous=NLON-1;
625 
626  int ilon_next=ilon+1;
627  if (ilon_next==NLON)
628  ilon_next=0;
629 
630  double r=(geoid[ilat]+surfacer[ilon][ilat])*sin(theta);
631 
632  double slope_phi=(surfacer[ilon_next][ilat]-surfacer[ilon_previous][ilat])/(r*2*phistep);
633 
634  int ilat_previous=ilat-1;
635  if (ilat_previous<0)
636  ilat_previous=0;
637 
638  int ilat_next=ilat+1;
639  if (ilat_next==NLAT)
640  ilat_next=NLAT-1;
641 
642  double slope_costheta=(surfacer[ilon][ilat_next]-surfacer[ilon][ilat_previous])/((geoid[ilat]+surfacer[ilon][ilat])*2*thetastep);
643 
644  // first rotate n_surf according to tilt in costheta and position on continent - rotate around the y axis.
645  double angle=atan(slope_costheta);
646 
647  n_surf = n_surf.RotateY(angle);
648 
649  // now rotate n_surf according to tilt in phi - rotate around the z axis.
650  angle=atan(slope_phi);
651 
652  n_surf = n_surf.RotateZ(angle);
653 
654  return n_surf;
655 
656 } //method GetSurfaceNormal
657 
658 double EarthModel::SmearPhi(int ilon, double rand) {
659 
660 
661  double phi=((double)(360.*3./4.-((double)ilon+rand)*360/180))*RADDEG;
662  if (phi<0 && phi>-1*PI/2)
663  phi+=2*PI;
664 
665 
666  return phi;
667 } //SmearPhi
668 
669 double EarthModel::SmearTheta(int ilat, double rand) {
670 
671  // remember that we should smear it evenly in cos(theta).
672  // first get the cos(theta)'s at the boundaries.
673 
674  double theta1=dGetTheta(ilat)-PI/(double)NLAT/2.;
675  double theta2=dGetTheta(ilat+1)-PI/(double)NLAT/2.;
676 
677  double costheta1=cos(theta1);
678  double costheta2=cos(theta2);
679 
680 
681 
682  double costheta=rand*(costheta2-costheta1)+costheta1;
683 
684  double theta=acos(costheta);
685 
686  return theta;
687 } //SmearTheta
688 
689 void EarthModel::ReadCrust(string test) {
690 
691  // reads in altitudes of 7 layers of crust, ice and water
692  // puts data in arrays
693 
694  fstream infile(test.c_str(),ios::in);
695 
696  string thisline; // for reading in file
697  string slon; //longitude as a string
698  string slat; // latitude as a string
699  string selev; // elevation (km relative to geoid)
700  string sdepth; // depth (km)
701  string sdensity; // density (g/cm^3)
702  double dlon,dlat; // longitude, latitude as double
703  int endindex; // index along thisline for parsing
704  int beginindex; // same
705 
706  int indexlon=0; // 180 bins in longitude
707  int indexlat=0; // 90 bins in latitude
708 
709  string layertype; // water, ice, etc.
710 
711  while(!infile.eof()) {
712  getline(infile,thisline,'\n');
713 
714  int loc=thisline.find("type, latitude, longitude,");
715 
716  if (loc!=(int)(string::npos)) {
717 
718  beginindex=thisline.find_first_not_of(" ",57);
719 
720  endindex=thisline.find_first_of(" ",61);
721 
722  slat=thisline.substr(beginindex,endindex-beginindex);
723  dlat=(double)atof(slat.c_str());
724 
725  beginindex=thisline.find_first_not_of(" ",68);
726  endindex=thisline.find_first_of(" ",72);
727 
728  slon=thisline.substr(beginindex,endindex-beginindex);
729  dlon=(double)atof(slon.c_str());
730 
731 
732  indexlon=(int)((dlon+180)/2);
733  indexlat=(int)((90+dlat)/2);
734 
735  beginindex=thisline.find_first_not_of(" ",78);
736  endindex=thisline.find_first_of(" ",83);
737 
738  selev=thisline.substr(beginindex,endindex-beginindex);
739  elevationarray[indexlon][indexlat]=(double)atof(selev.c_str());
740 
741  } //if
742 
743  for (int i=0;i<4;i++) {
744  getline(infile,thisline,'\n');
745  } //for
746 
747  for (int i=0;i<7;i++) {
748  getline(infile,thisline,'\n');
749 
750  endindex=thisline.length()-1;
751  beginindex=thisline.find_last_of("0123456789",1000);
752  layertype=thisline.substr(beginindex+3,endindex-beginindex);
753 
754 
755  beginindex=thisline.find_first_not_of(" ",0);
756  endindex=thisline.find_first_of(" ",beginindex);
757 
758  sdepth=thisline.substr(beginindex,endindex-beginindex-1);
759 
760 
761  // fills arrays of thicknesses of each layer
762  if (layertype.substr(0,5)=="water")
763  waterthkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
764  if (layertype.substr(0,3)=="ice")
765  icethkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
766  if (layertype.substr(0,8)=="soft sed")
767  softsedthkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
768  if (layertype.substr(0,8)=="hard sed")
769  hardsedthkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
770  if (layertype.substr(0,11)=="upper crust")
771  uppercrustthkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
772  if (layertype.substr(0,12)=="middle crust")
773  middlecrustthkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
774  if (layertype.substr(0,11)=="lower crust")
775  lowercrustthkarray[indexlon][indexlat]=(double)atof(sdepth.c_str());
776 
777  // cout << "indexlon, indexlat, icethkarray " << indexlon << " " << indexlat << " " << icethkarray[indexlon][indexlat] << "\n";
778 
779  // region where Ross Ice Shelf was not accounted for in Crust 2.0
780  // add it in by hand
781  if (indexlat==5 && (indexlon<=5 || indexlon>=176)) // Ross Ice Shelf
782  icethkarray[indexlon][indexlat]=0.5;
783 
784  beginindex=thisline.find_first_not_of(" ",endindex);
785  endindex=thisline.find_first_of(" ",beginindex);
786 
787 
788  beginindex=thisline.find_first_not_of(" ",endindex);
789  endindex=thisline.find_first_of(" ",beginindex);
790 
791  beginindex=thisline.find_first_not_of(" ",endindex);
792  endindex=thisline.find_first_of(" ",beginindex);
793 
794 
795  sdensity=thisline.substr(beginindex,endindex-beginindex);
796 
797  double ddensity=(double)atof(sdensity.c_str());
798 
799 
800  // fills arrays of densities of each layer
801  if (layertype.substr(0,5)=="water")
802  waterdensityarray[indexlon][indexlat]=ddensity;
803  if (layertype.substr(0,3)=="ice")
804  icedensityarray[indexlon][indexlat]=ddensity;
805  if (layertype.substr(0,8)=="soft sed")
806  softseddensityarray[indexlon][indexlat]=ddensity;
807  if (layertype.substr(0,8)=="hard sed")
808  hardseddensityarray[indexlon][indexlat]=ddensity;
809  if (layertype.substr(0,11)=="upper crust")
810  uppercrustdensityarray[indexlon][indexlat]=ddensity;
811  if (layertype.substr(0,12)=="middle crust")
812  middlecrustdensityarray[indexlon][indexlat]=ddensity;
813  if (layertype.substr(0,11)=="lower crust")
814  lowercrustdensityarray[indexlon][indexlat]=ddensity;
815  } //for (reading all lines for one location given in Crust 2.0 input file)
816 
817  if (CONSTANTCRUST) {
818  softsedthkarray[indexlon][indexlat]=40.;
819  hardsedthkarray[indexlon][indexlat]=0;
820  uppercrustthkarray[indexlon][indexlat]=0;
821  middlecrustthkarray[indexlon][indexlat]=0;
822  lowercrustthkarray[indexlon][indexlat]=0;
823  crustthkarray[indexlon][indexlat]=0;
824  softseddensityarray[indexlon][indexlat]=2.9;
825  } //if (set crust thickness to constant everywhere)
826  if (CONSTANTICETHICKNESS) {
827  icethkarray[indexlon][indexlat]=3.;
828  waterthkarray[indexlon][indexlat]=0.;
829  } //if (set ice thickness to constant everywhere)
830 
831  // adds up total thickness of crust
832  crustthkarray[indexlon][indexlat]=softsedthkarray[indexlon][indexlat]+
833  hardsedthkarray[indexlon][indexlat]+
834  uppercrustthkarray[indexlon][indexlat]+
835  middlecrustthkarray[indexlon][indexlat]+
836  lowercrustthkarray[indexlon][indexlat];
837 
838  if (indexlon==179 && indexlat==0)
839  break;
840  } // done reading file
841 
842  for (int i=0;i<NLON;i++) {
843  for (int j=0;j<NLAT;j++) {
844 
845  if (FIXEDELEVATION)
846  elevationarray[i][j]=icethkarray[i][j]*1000;
847 
848  if (waterthkarray[i][j] != 0)
849  surfacer[i][j]=elevationarray[i][j]+waterthkarray[i][j]*1000+icethkarray[i][j]*1000;
850  else
851  surfacer[i][j]=elevationarray[i][j];
852 
853  if (fabs(surfacer[i][j])<1.E-10)
854  surfacer[i][j] = 0;
855 
856  // reminder- waterr is elevation at *bottom* of water layer, etc.
857  // in units of m
858  waterr[i][j]=surfacer[i][j]-(icethkarray[i][j]+waterthkarray[i][j])*1000;
859  if ((double)fabs(waterr[i][j])<1.E-10)
860  waterr[i][j]=0;
861  icer[i][j]=waterr[i][j]+
862  waterthkarray[i][j]*1000;
863  softsedr[i][j]=waterr[i][j]-
864  softsedthkarray[i][j]*1000;
865  hardsedr[i][j]=waterr[i][j]-
866  (softsedthkarray[i][j]+
867  hardsedthkarray[i][j])*1000;
868  uppercrustr[i][j]=waterr[i][j]-
869  (softsedthkarray[i][j]+
870  hardsedthkarray[i][j]+
871  uppercrustthkarray[i][j])*1000;
872  middlecrustr[i][j]=waterr[i][j]-
873  (softsedthkarray[i][j]+
874  hardsedthkarray[i][j]+
875  uppercrustthkarray[i][j]+
876  middlecrustthkarray[i][j])*1000;
877  lowercrustr[i][j]=waterr[i][j]-
878  (softsedthkarray[i][j]+
879  hardsedthkarray[i][j]+
880  uppercrustthkarray[i][j]+
881  middlecrustthkarray[i][j]+
882  lowercrustthkarray[i][j])*1000;
883  } //for (latitude bins)
884  } //for (longitude bins)
885 
886  // calculate ice volume for comparison with expectation
887  volume=0;
888  ice_area=0.;
889  double sumarea=0; // sum of surface area of ice
890  average_iceth = 0;
891  max_icevol_perbin=0.;
892  max_icethk_perbin=0.;
893  for (int j=0;j<ILAT_COASTLINE;j++) {
894  for (int i=0;i<NLON;i++) {
895  volume+=icethkarray[i][j]*1000.*area[j];
896  if (icethkarray[i][j]>0)
897  ice_area+=area[j];
898  if (icethkarray[i][j]*area[j]>max_icevol_perbin)
899  max_icevol_perbin=icethkarray[i][j]*area[j];
900  if (icethkarray[i][j]>max_icethk_perbin)
901  max_icethk_perbin=icethkarray[i][j];
902 
903  /*
904  // j=6 corresponds to 80deg S
905  if (j==6) {
906  // fill output file, just for plotting
907  outfile << surfacer[i][j] << "\t" << waterr[i][j] << "\t" << icer[i][j] << "\t" << icethkarray[i][j] << " " << waterr[i][j]-icer[i][j] << " " << (waterr[i][j]-icer[i][j])*area[j] << " " << area[j] << " " << volume << "\n";
908  }//ifx
909  */
910 
911  // find average ice thickness
912  average_iceth+=(surfacer[i][j]-icer[i][j])*area[j];
913  sumarea+=area[j];
914  } //for
915  } //for
916  average_iceth=average_iceth/sumarea;
917 
918  // find the place where the crust is the deepest.
919  // for finding where to start stepping in Getchord
920  MIN_ALTITUDE_CRUST=1.E6;
921  //MAX_VOL=-1.E6;
922  for (int i=0;i<NLON;i++) {
923  for (int j=0;j<NLAT;j++) {
924  if (elevationarray[i][j]-(crustthkarray[i][j])*1000<MIN_ALTITUDE_CRUST) {
925  if (waterthkarray[i][j]==0)
926  MIN_ALTITUDE_CRUST=elevationarray[i][j]-(crustthkarray[i][j]+icethkarray[i][j])*1000;
927  else
928  MIN_ALTITUDE_CRUST=elevationarray[i][j]-crustthkarray[i][j]*1000;
929  }//if
930  //if (icethkarray[i][j]*1000.*area[j]>MAX_VOL)
931  //MAX_VOL=icethkarray[i][j]*1000.*area[j];
932  }//for
933  }//for
934 
935  //record depth of crust-mantle interface
936  radii[1]=(Geoid(0.)+MIN_ALTITUDE_CRUST)*(Geoid(0.)+MIN_ALTITUDE_CRUST);
937 
938 }//ReadCrust
939 
940 
941 Vector EarthModel::PickPosnuForaLonLat(double lon,double lat,double theta,double phi) {
942 
943 
944  double surface_above_geoid = this->SurfaceAboveGeoid(lon,lat);
945  double local_ice_thickness = this->IceThickness(lon,lat);
946 
947  double rnd3=getRNG(RNG_POSNU)->Rndm();
948 
949 
950 
951  double elevation = surface_above_geoid - rnd3*local_ice_thickness; // elevation of interaction
952 
953 
954 
955  //cout << "Inside PickInteractionLocation, lon, lat, local_ice_thickness are " << lon << " " << lat << " " << local_ice_thickness << "\n";
956 
957  if (elevation > surface_above_geoid || elevation < (surface_above_geoid - local_ice_thickness))
958  cout<<"elevation > surface_above_geoid || elevation < (surface_above_geoid - local_ice_thickness)\n";
959 
960  Vector posnu((elevation+this->Geoid(lat))*sin(theta)*cos(phi),(elevation+this->Geoid(lat))*sin(theta)*sin(phi),(elevation+this->Geoid(lat))*cos(theta));
961 
962  if (((this->Geoid(lat) + surface_above_geoid)) - posnu.Mag() < 0) {
963  //cout<<"/nYikes! (Geoid(lat) + Surface(lon,lat)) - sqrt(dSquare(posnu) = "<<((this->Geoid(lat) + surface_above_geoid)) - posnu.Mag()<<"/nlon, lat: "<<lon<<" , "<<lat<< " " << endl<<endl;
964  //cout << "local_ice_thickness is " << local_ice_thickness << "\n";
965  }
966 
967  return posnu;
968 }
969 
970 double EarthModel::dGetTheta(int ilat) {
971  return (((double)ilat+0.5)/(double)NLAT*MAXTHETA)*RADDEG;
972 } //dGetTheta(int)
973 
974 double EarthModel::dGetPhi(int ilon) {
975  // this takes as an input the crust 2.0 index 0=-180 deg longitude to 179=+180 deg longitude
976  // its output is phi in radians
977  // from ~ -pi/2 to 3*pi/2
978  return (double)(-1*((double)ilon+0.5)+(double)NLON)*2*PI/(double)NLON-PI/2;
979 } //dGetPhi(int)
980 
981 void EarthModel::GetILonILat(const Position &p,int& ilon,int& ilat) {
982  // Phi function outputs from 0 to 2*pi wrt +x
983  double phi_deg=p.Phi()*DEGRAD;
984 
985  if (phi_deg>270)
986  phi_deg=phi_deg-360;
987  // now it's from -90 to 270
988 
989  ilon=(int)((360.*3./4.-phi_deg)*180./360.); // ilon is from 0 (at -180 longitude) to 180 (at 180 longitude)
990 
991  ilat=(int)((p.Theta()*DEGRAD)/2.);
992 
993 } //method GetILonILat
994 void EarthModel::EarthCurvature(double *array,double depth_temp) {
995 
996  Position parray;
997  parray.SetXYZ(array[0],array[1],array[2]);
998 
999  // adjust array coordinates so that it fits to a curved earth surface at a specific depth
1000  double length=Surface(parray)-depth_temp; // length=distance from center of earth
1001 
1002  double rxposx=array[0]; // x coordinate of antenna position
1003  double rxposy=array[1]; // y coordinate of antenna position
1004  double rxdr=sqrt(rxposx*rxposx+rxposy*rxposy); // distance in horizontal plane from the center of the detector to the antenna
1005  if (Tools::dSquare(array)==0) cout << "Attempt of divide by zero in Earth curvature!!\n";
1006  double rxdtheta=asin(rxdr/sqrt(Tools::dSquare(array)));
1007  double rxdphi=atan2(rxposy,rxposx);
1008 
1009  array[0]=length*sin(rxdtheta)*cos(rxdphi);// have the array sit on a sphere of radius "length"
1010  array[1]=length*sin(rxdtheta)*sin(rxdphi);
1011  array[2]=length*cos(rxdtheta);
1012 
1013 }
1014 Position EarthModel::WhereDoesItEnter(const Position &posnu,const Vector &nnu) {
1015  // now get neutrino entry point...
1016  double p = posnu.Mag(); // radius of interaction
1017  double costheta = (nnu*posnu) / p; // theta of neutrino at interaction position
1018  double sintheta = sqrt(1-costheta*costheta);
1019 
1020  double lon = posnu.Lon();
1021  double lat = posnu.Lat();
1022 
1023  double a=0; // length of chord
1024 
1025  double R = Surface(lon,lat);
1026  double delta = R - p; // depth of the interaction
1027  // if interaction occurs below surface, as it should
1028 
1029  if (delta>-0.001) {
1030  a=p*costheta+sqrt(R*R*costheta*costheta+2*delta*R*sintheta*sintheta); // chord length
1031  if (a<0) {
1032  cout << "Negative chord length: " << a << "\n";
1033  } //end if
1034  } //end if (interaction below surface)
1035  else if (delta<=-0.001) {
1036 
1037  cout << "lon, lat from WhereDoesItEnter is " << lon << " " << lat << "\n";
1038  cout << "geoid, surface, p, surface-p are " << Geoid(lat) << " " << Surface(lon,lat) << " " << p << " , "<<(Surface(lon,lat)-p)<<"\n";
1039 
1040  } //else if: error: interaction takes place above the surface
1041 
1042 
1043  // first approx
1044  // find where nnu intersects spherical earth surface
1045  Position r_in = posnu - a*nnu;
1046 
1047  int iter = 0;
1048  // now do correction 3 times
1049  //for (iter=0; iter<3; iter++) {
1050  // delta = r_in.Mag() - antarctica1->Surface( r_in );
1051  // r_in = r_in + (delta * nnu);
1052  //}
1053 
1054  // how far away r_in is from surface, along line to center of earth
1055  delta = r_in.Mag() - Surface( r_in );
1056 
1057  while ( fabs(delta) >= 0.1 ) { //if it's greater than 10 cm
1058  r_in = r_in + (delta * nnu); // move distance delta along nnu for the next iteration
1059  delta = r_in.Mag() - Surface( r_in ); // find new delta
1060  iter++;
1061  if ( iter > 10 ) { // stop after 10 iterations and just revert to simple answer
1062  //cout<<"\n r_in iteration more than 10 times!!! delta : "<<delta<<". now set r_in as before."<<endl;
1063  r_in = Surface( r_in ) * r_in.Unit(); // the way before
1064  delta = r_in.Mag() - Surface( r_in );
1065  }
1066  }
1067 
1068 
1069  //lon = r_in.Lon();
1070  //lat = r_in.Lat();
1071 
1072  //r_in = antarctica1->Surface(lon,lat) * r_in.Unit();
1073 
1074 
1075  return r_in;
1076 } //method WhereDoesItEnter
1077 
1078 
1079 int EarthModel::GeoidIntersection(Vector x0,Vector p0, Position * int1, Position * int2, double extra_height, double * ds) const
1080 {
1081 
1082 
1110  double POLAR_RADIUS = 6356752.31425 + extra_height;
1111  double EQUATORIAL_RADIUS = 6378137.0 + extra_height;
1112  double EQ2 = EQUATORIAL_RADIUS*EQUATORIAL_RADIUS;
1113  double PO2 = POLAR_RADIUS*POLAR_RADIUS;
1114  double RAT = EQ2/PO2;
1115 
1116 
1117 
1118  //d^2 terms
1119  double a = p0.X()*p0.X() + p0.Y()*p0.Y() + p0.Z()*p0.Z() *RAT;
1120 
1121  //d terms
1122  double b = 2 * ( p0.X()*x0.X() + x0.Y()*p0.Y() + x0.Z()*p0.Z() *RAT) ;
1123 
1124  //constant terms
1125  double c = -EQ2 + x0.X()*x0.X() + x0.Y()*x0.Y() + x0.Z()*x0.Z() *RAT;
1126 
1127  //check discriminant
1128  double discr = b*b-4*a*c;
1129 
1130  //no solution
1131  if (discr < 0)
1132  {
1133  return 0;
1134  }
1135 
1136  discr = sqrt(discr);
1137 
1138  double d0= (-b + discr)/(2*a);
1139  double d1= (-b - discr)/(2*a);
1140  if (ds)
1141  {
1142  ds[0] = d0;
1143  ds[1] = d1;
1144  }
1145 
1146  if (int1)
1147  {
1148  int1->SetXYZ(
1149  x0.X() + d0 *p0.X(),
1150  x0.Y() + d0 *p0.Y(),
1151  x0.Z() + d0 *p0.Z() );
1152 
1153  }
1154  if (int2)
1155  {
1156  int2->SetXYZ(
1157  x0.X() + d1 *p0.X(),
1158  x0.Y() + d1 *p0.Y(),
1159  x0.Z() + d1 *p0.Z() );
1160 
1161  }
1162 
1163  return discr == 0 ? 1 : 2;
1164 }
const char * ICEMC_SRC_DIR()
Reads in and stores input settings for the run.
Definition: Settings.h:35
double Lat() const
Returns latitude, where the +z direction is at 0 latitude.
Definition: position.cc:47
This class is a 3-vector that represents a position on the Earth&#39;s surface.
Definition: position.hh:26
int GeoidIntersection(Vector x0, Vector p0, Position *int1, Position *int2, double extra_height=5500, double *ds=0) const
Definition: earthmodel.cc:1079
This class represents a three-vector. Operators are overloaded to provide for the familiar operations...
Definition: vector.hh:27
double Distance(const Position &second) const
Returns chord distance (direct distance between two vectors)
Definition: position.cc:37
double Lon() const
Returns longitude.
Definition: position.cc:51