EarthModel.cxx
Go to the documentation of this file.
1 
2 
3 
4 
5 
6 
7 #include "OscLib/func/EarthModel.h"
8 #include "Utilities/func/MathUtil.h"
9 #include <cstdlib>
10 #include <iostream>
11 #include <cmath>
12 #include <list>
13 using namespace osc;
14 
15 static const int kPREM = 0;
16 static const int kSTACEY = 1;
17 
18 EarthModel::EarthModel(const char* which, double tol)
19 {
20  if (std::string("PREM") ==which) this->InitPREM();
21  else if (std::string("STACEY")==which) this->InitStacey();
22  // else if (std::string("ModelX")==which) this->InitModelX();
23  else {
24  std::cerr << __FILE__ << ":" << __LINE__
25  << " Model '" << which << "' is not supported."
26  << " Currently only PREM is supported." << std::endl;
27  abort();
28  }
29  this->MakeLayers(tol);
30 }
31 
32 //......................................................................
33 double EarthModel::Ne(double r)
34 {
35  return this->ZoverA(r)*this->Density(r);
36 }
37 
38 //......................................................................
39 double EarthModel::Density(double r)
40 {
41  switch (fModelID) {
42  case kPREM: return this->DensityPREM(r);
43  case kSTACEY: return this->DensityStacey(r);
44  default: abort();
45  }
46  return 0.0;
47 }
48 
49 //......................................................................
51 {
52  fModelID = kPREM;
53  fRouterCore = 3480.0;
54  fRearth = 6371.0;
55 
56  fRregion.push_back(0.0);
57  fRregion.push_back(1221.5); // Inner core
58  fRregion.push_back(3480.0); // Outer core
59  fRregion.push_back(3630.0); // D''
60  fRregion.push_back(5701.0); // Lower mantle
61  fRregion.push_back(6151.0); // Transition zone
62  fRregion.push_back(6291.0); // Low velocity zone
63  fRregion.push_back(6346.6); // LID
64  fRregion.push_back(6368.0); // Crust
65  fRregion.push_back(6371.0); // Ocean
66 }
67 
68 //......................................................................
69 double EarthModel::DensityPREM(double r)
70 {
71  double x = r/fRearth;
72 
73  // The regions are checked in order from largest to smallest
74  if (r>1221.5&&r<=3480.0) return 12.5815+x*(-1.2638+x*(-3.6426+x*(-5.5281)));
75  if (r>3480.0&&r<=5701.0) return 7.9565+x*(-6.4761+x*(5.5283+x*(-3.0807)));
76  if (r<=1221.5) return 13.0885-8.8381*x*x;
77  if (r>5771.0&&r<=5971.0) return 11.2494-8.0298*x;
78  if (r>5971.0&&r<=6151.0) return 7.1089-3.8045*x;
79  if (r>6151.0&&r<=6291.0) return 2.6910+0.6924*x;
80  if (r>5701.0&&r<=5771.0) return 5.3197-1.4836*x;
81  if (r>6291.0&&r<=6346.6) return 2.6910+0.6924*x;
82  if (r>6346.6&&r<=6356.0) return 2.90;
83  if (r>6356.0&&r<=6368.0) return 2.60;
84  if (r>6368.0&&r<=6371.0) return 1.020;
85  return 0.0;
86 }
87 
88 //......................................................................
90 {
91  fModelID = kSTACEY;
92  fRouterCore = 6371.0-2878.0;
93  fRearth = 6371.0;
94 
95  fRregion.push_back(0.0);
96  fRregion.push_back(6371.0-5121.0); // Inner core
97  fRregion.push_back(6371.0-2878.0); // Outer core
98  fRregion.push_back(6371.0-650.0); // Lower mantle
99  fRregion.push_back(6371.0-350.0); // Middle mantle
100  fRregion.push_back(6371.0-15.0); // Upper mantle
101  fRregion.push_back(6371.0); // Crust
102 }
103 
104 //......................................................................
106 {
107  static double crustD[2] = { 0.0,
108  15.0};
109  static double upperMantleD[6] = { 15.0,
110  60.0,
111  100.0,
112  200.0,
113  300.0,
114  350.0};
115  static double middleMantleD[6] = {350.0,
116  400.0,
117  413.0,
118  500.0,
119  600.0,
120  650.0};
121  static double lowerMantleD[14] = {650.0,
122  800.0,
123  984.0,
124  1000.0,
125  1200.0,
126  1400.0,
127  1600.0,
128  1800.0,
129  2000.0,
130  2200.0,
131  2400.0,
132  2600.0,
133  2800.0,
134  2878.0};
135  static double outerCoreD[14] = { 2878.0,
136  3000.0,
137  3200.0,
138  3400.0,
139  3600.0,
140  3800.0,
141  4000.0,
142  4200.0,
143  4400.0,
144  4600.0,
145  4800.0,
146  4982.0,
147  5000.0,
148  5121.0};
149  static double innerCoreD[8] ={ 5121.0,
150  5200.0,
151  5400.0,
152  5600.0,
153  5800.0,
154  6000.0,
155  6200.0,
156  6371.0};
157  static double crustRho[2] = { 2.840,
158  2.840};
159  static double upperMantleRho[6] = { 3.313,
160  3.332,
161  3.348,
162  3.387,
163  3.424,
164  3.441};
165  static double middleMantleRho[6] = {3.700,
166  3.775,
167  3.795,
168  3.925,
169  4.075,
170  4.150};
171  static double lowerMantleRho[14] = {4.200,
172  4.380,
173  4.529,
174  4.538,
175  4.655,
176  4.768,
177  4.877,
178  4.983,
179  5.087,
180  5.188,
181  5.288,
182  5.387,
183  5.487,
184  5.527};
185  static double outerCoreRho[14] = { 9.927,
186  10.121,
187  10.421,
188  10.697,
189  10.948,
190  11.176,
191  11.383,
192  11.570,
193  11.737,
194  11.887,
195  12.017,
196  12.121,
197  12.130,
198  12.197};
199  static double innerCoreRho[7] = { 12.229,
200  12.301,
201  12.360,
202  12.405,
203  12.437,
204  12.455,
205  12.460};
206  // ============ END DENSITY TABLE ============
207 
208  if (r>fRearth) return 0.0;
209  double d = fRearth-r; // Table uses depth
210  register int i;
211 
212  if (d>=crustD[0] && d<crustD[1]) {
213  for (i=0; i<1; ++i) {
214  if (d>=crustD[i] && d<crustD[i+1]) {
215  return crustRho[i];
216  }
217  }
218  }
219  if (d>=upperMantleD[0] && d<upperMantleD[5]) {
220  for (i=0; i<5; ++i) {
221  if (d>=upperMantleD[i] && d<upperMantleD[i+1]) {
222  return upperMantleRho[i];
223  }
224  }
225  }
226  if (d>=middleMantleD[0] && d<middleMantleD[5]) {
227  for (i=0; i<5; ++i) {
228  if (d>=middleMantleD[i] && d<middleMantleD[i+1]) {
229  return middleMantleRho[i];
230  }
231  }
232  }
233  if (d>=lowerMantleD[0] && d<lowerMantleD[13]) {
234  for (i=0; i<13; ++i) {
235  if (d>=lowerMantleD[i] && d<lowerMantleD[i+1]) {
236  return lowerMantleRho[i];
237  }
238  }
239  }
240  if (d>=outerCoreD[0] && d<outerCoreD[13]) {
241  for (i=0; i<13; ++i) {
242  if (d>=outerCoreD[i] && d<outerCoreD[i+1]) {
243  return outerCoreRho[i];
244  }
245  }
246  }
247  if (d>=innerCoreD[0] && d<innerCoreD[7]) {
248  for (i=0; i<7; ++i) {
249  if (d>=innerCoreD[i] && d<innerCoreD[i+1]) {
250  return innerCoreRho[i];
251  }
252  }
253  }
254  return 0.0;
255 }
256 
257 //......................................................................
258 double EarthModel::ZoverA(double r)
259 {
260  if (r<fRouterCore) return 0.468;
261  return 0.497;
262 }
263 
264 //......................................................................
265 
266 double EarthModel::AveNe(double r1, double r2, int nsample)
267 {
268  double sum = 0.0;
269  double r;
270  for (int i=0; i<nsample; ++i) {
271  r = r1 + (r2-r1)*((float)i+0.5)/(float)nsample;
272  sum += this->Ne(r);
273  }
274  return sum/(float)nsample;
275 }
276 
277 //......................................................................
278 void EarthModel::MakeLayers(double tol)
279 {
280  unsigned int nsample = 20;
281  for (unsigned int i=0; i<fRregion.size()-1; ++i) {
282  // Add layers within each region until all layers are within
283  // tolerance
284  double rRegionLo = fRregion[i];
285  double rRegionHi = fRregion[i+1];
286  double r1 = rRegionLo;
287  double r2 = rRegionHi;
288  double r;
289  double ne;
290  while (1) {
291  double ave = this->AveNe(r1, r2, nsample);
292  bool isok = true;
293  for (unsigned int j=0; j<nsample; ++j) {
294  r = r1+(r2-r1)*((float)j+0.5)/(float)nsample;
295  ne = this->Ne(r);
296  if (fabs(ne-ave)/ne>tol) { isok = false; break; }
297  }
298  if (isok) {
299  // Layer is within tolerance - accept it
300  fRlo.push_back(r1);
301  fRhi.push_back(r2);
302  fNe. push_back(ave);
303  if (r2>=rRegionHi) {
304  // Finished subdividing this region
305  break;
306  }
307  else {
308  // Ready next iteration of subdivision
309  r1 = r2;
310  r2 = rRegionHi;
311  }
312  } // if (isok) ...
313  else {
314  // Layer is not within tolerance - reduce its size
315  r2 -= 1.0; // Step back some distance (km) and try again
316  if (r2<=r1) r2 = r1+0.5;
317  }
318  } // making layers within region
319  } // loop on regions
320 }
321 
322 //......................................................................
323 
324 void EarthModel::GetLayers(std::vector<double>& rlo,
325  std::vector<double>& rhi,
326  std::vector<double>& ne)
327 {
328  rlo.resize(fRlo.size());
329  rhi.resize(fRlo.size());
330  ne. resize(fRlo.size());
331  for (unsigned int i=0; i<fRlo.size(); ++i) {
332  rlo[i] = fRlo[i];
333  rhi[i] = fRhi[i];
334  ne[i] = fNe[i];
335  }
336 }
337 
338 //......................................................................
339 void EarthModel::LineProfile(double anu,
340  double cosQ,
341  double adet,
342  std::list<double>& Ls,
343  std::list<double>& Ns)
344 {
345  // A typical number density for the crust
346  static const double crustNe = 2.6*0.5;
347 
348  // Locations are relative to a cicular Earth centered at (0,0)
349  double rdet; // Radial location of detector (km)
350  double rnu; // Radial location of neutrino (km)
351  double Lnu; // Total flight distance of neutrino (km)
352  double x1; // Location of detector (km)
353  double y1; // Location of detector (km)
354  double x2; // Location of neutrino production (km)
355  double y2; // Location of neutrino production (km)
356 
357  rdet = fRearth+adet;
358  rnu = fRearth+anu;
359  Lnu = rdet*(sqrt(util::sqr(rnu/rdet)+cosQ*cosQ-1.0)-cosQ);
360 
361  x2 = 0.0;
362  y2 = rdet;
363  x1 = x2+sqrt(1.0-cosQ*cosQ)*Lnu;
364  y1 = y2+cosQ*Lnu;
365 
366  unsigned int i;
367  int n1, n2;
368  double L;
369  double xina, yina;
370  double xouta, youta;
371  double xinb, yinb;
372  double xoutb, youtb;
373  Ls.clear();
374  Ns.clear();
375 
376  //
377  // For down-going neutrinos, only consider matter in the top layer
378  //
379  if (cosQ>=0.0) {
380  if (adet>0.0) {
381  Ls.push_back(Lnu);
382  Ns.push_back(0.0);
383  }
384  else {
385  n1 = this->IntersectLineAndCircle(x1, y1, x2, y2, fRearth,
386  &xouta, &youta, &xoutb, &youtb);
387  L = util::pythag(x1-xoutb, y1-youtb);
388  Ls.push_back(L);
389  Ns.push_back(0.0);
390 
391  L = util::pythag(x2-xoutb, y2-youtb);
392  Ls.push_back(L);
393  Ns.push_back(crustNe); // Typical rock density
394  }
395  return;
396  }
397 
398  //
399  // For up-going neutrinos, find the intersections of the neutrino's
400  // path with all layers of the Earth model.
401  //
402  for (i=0; i<fRhi.size()-1; ++i) {
403  n1 = this->IntersectLineAndCircle(x1, y1, x2, y2, fRhi[i],
404  &xouta, &youta, &xoutb, &youtb);
405 
406  n2 = this->IntersectLineAndCircle(x1, y1, x2, y2, fRlo[i],
407  &xina, &yina, &xinb, &yinb);
408  //
409  // Create segments for each layer we intersect
410  //
411  if (n1==2 && n2<2) {
412  //
413  // Intersect outer radius, but not inner radius:
414  // Only one one segment to create.
415  //
416  L = util::pythag(xouta-xoutb, youta-youtb);
417 
418  Ls.push_back(L);
419  Ns.push_back(fNe[i]);
420  }
421  else if (n1==2 && n2==2) {
422  //
423  // Path intersects both inner and outer radii: Two sections to
424  // create, one on the way in, one on the way out.
425  //
426  L = util::pythag(xouta-xina, youta-yina);
427  Ls.push_front(L);
428  Ns.push_front(fNe[i]);
429 
430  L = util::pythag(xoutb-xinb, youtb-yinb);
431  Ls.push_back(L);
432  Ns.push_back(fNe[i]);
433  }
434  } // Loop on all but last layer
435 
436  //
437  // Handle the last layer specially
438  //
439  i = fRhi.size()-1;
440  n1 = this->IntersectLineAndCircle(x1, y1, x2, y2, fRhi[i],
441  &xouta, &youta, &xoutb, &youtb);
442  n2 = this->IntersectLineAndCircle(x1, y1, x2, y2, fRlo[i],
443  &xina, &yina, &xinb, &yinb);
444  if (n1==2 && n2<2) {
445  //
446  // Intersect outer radius, but not inner radius:
447  // One one segment to create
448  //
449  if (youtb>y2) {
450  //
451  // Don't over shoot the detector location
452  //
453  L = util::pythag(xouta-x2, youta-y2);
454  }
455  else {
456  L = util::pythag(xouta-xoutb, youta-youtb);
457  }
458  Ls.push_back(L);
459  Ns.push_back(fNe[i]);
460  }
461  else if (n1==2 && n2==2) {
462  //
463  // Path intersects both inner and outer radii: Two sections to
464  // create, one on the way in, one on the way out.
465  //
466  L = util::pythag(xouta-xina, youta-yina);
467  Ls.push_front(L);
468  Ns.push_front(fNe[i]);
469 
470  if (youtb>y2) {
471  //
472  // Don't over shoot the detector location
473  //
474  L = util::pythag(x2-xinb, y2-yinb);
475  }
476  else {
477  L = util::pythag(xoutb-xinb, youtb-yinb);
478  }
479  Ls.push_back(L);
480  Ns.push_back(fNe[i]);
481  }
482  //
483  // Add segment to get neutrino from production to Earth's surface
484  //
485  L = util::pythag(xouta-x1, youta-y1);
486  Ls.push_front(L);
487  Ns.push_front(0.0); // Density of air~=0
488 
489  //
490  // If required, add segment to get from sea level up to the
491  // detector's position
492  //
493  if (youtb<y2) {
494  L = util::pythag(xoutb-x2, youtb-y2);
495  Ls.push_back(L);
496  Ns.push_back(crustNe); // Density of of standard rock (mole/cm^3)
497  }
498 
499  //
500  // The following is a useful piece of debugging - check that we've
501  // accounted for all pieces of the neutrino flight distance
502  //
503  if (false) {
504  std::list<double>::iterator itr(Ls.begin());
505  std::list<double>::iterator itrEnd(Ls.end());
506  double ltot = 0.0;
507  for (; itr!=itrEnd; ++itr) {
508  ltot += (*itr);
509  }
510  std::cout << ltot << " " << Lnu << std::endl;
511  if (fabs(ltot-Lnu)>1e-6) abort();
512  }
513 }
514 
515 //......................................................................
516 
517 int EarthModel::IntersectLineAndCircle(double x1, double y1,
518  double x2, double y2,
519  double r,
520  double* xa, double* ya,
521  double* xb, double* yb)
522 {
523  double dx = x2-x1;
524  double dy = y2-y1;
525  double drsqr = dx*dx + dy*dy;
526  double D = x1*y2 - x2*y1;
527  double arg = r*r*drsqr - D*D;
528  if (arg<0.0) return 0;
529 
530  double sgndy = 1.0;
531  if (dy<0.0) sgndy = -1.0;
532 
533  *xa = ( D*dy - sgndy * dx * sqrt(arg) ) / drsqr;
534  *ya = (-D*dx - fabs(dy) * sqrt(arg) ) / drsqr;
535  *xb = ( D*dy + sgndy * dx * sqrt(arg) ) / drsqr;
536  *yb = (-D*dx + fabs(dy) * sqrt(arg) ) / drsqr;
537 
538  if (arg==0.0) return 1;
539  return 2;
540 }
541 
intermediate_table::iterator iterator
EarthModel(const char *which, double tol)
Definition: EarthModel.cxx:18
std::string string
Definition: nybbler.cc:12
std::vector< double > fNe
Definition: EarthModel.h:51
double fRearth
Definition: EarthModel.h:46
std::vector< double > fRregion
Definition: EarthModel.h:47
const double rdet
Definition: LBNENuWeight.cc:13
#define D
Debug message.
Definition: tclscanner.cpp:775
double fRouterCore
Definition: EarthModel.h:45
void resize(Vector< T > &vec1, Index n1, const V &val)
void MakeLayers(double tol)
Definition: EarthModel.cxx:278
double DensityStacey(double r)
Definition: EarthModel.cxx:105
T sqr(T v)
void GetLayers(std::vector< double > &rlo, std::vector< double > &rhi, std::vector< double > &ne)
Definition: EarthModel.cxx:324
int IntersectLineAndCircle(double x1, double y1, double x2, double y2, double r, double *xa, double *ya, double *xb, double *yb)
Definition: EarthModel.cxx:517
double Ne(double r)
Definition: EarthModel.cxx:33
void LineProfile(double prodL, double cosQ, double rdet, std::list< double > &Ls, std::list< double > &Ns)
Definition: EarthModel.cxx:339
double AveNe(double r1, double r2, int nsample)
Definition: EarthModel.cxx:266
Definition: EarthModel.h:12
double DensityPREM(double r)
Definition: EarthModel.cxx:69
std::vector< double > fRlo
Definition: EarthModel.h:49
double ZoverA(double r)
Definition: EarthModel.cxx:258
std::vector< double > fRhi
Definition: EarthModel.h:50
double Density(double r)
Definition: EarthModel.cxx:39
list x
Definition: train.py:276
static const int kPREM
Definition: EarthModel.cxx:15
static const int kSTACEY
Definition: EarthModel.cxx:16
QTextStream & endl(QTextStream &s)